Алгоритм — ваша торговая идея, записанная кодом так, чтобы терминал выполнял её сам. Здесь всё, чтобы написать свой: с чего начать, как код связан с терминалом, какие события он слышит, что читает и что может сделать.
Все примеры кода в этой документации проверяются при каждой сборке Let's Go: они компилируются и прогоняются на тестовой ленте сделок. Если пример здесь есть, он работает. Кнопка «Открыть в редакторе» у примера создаёт из него новый алгоритм.
Начало: первый алгоритм за 5 минут
Задача: держать лимитную покупку на 0,5% ниже цены и переставлять её, когда цена уходит.
Откройте панель «Алгоритмы» (кнопка «+» у вкладок рабочей области).
Нажмите «+» → «Пустой». Откроется каркас с пустыми обработчиками.
Замените его этим кодом (или нажмите «Открыть в редакторе» у примера):
Внизу редактора — зелёная галочка и параметры: код без ошибок. Ошибка подчёркивается красной волной прямо в строке, текст ошибки — внизу.
Нажмите «Сохранить», затем «Запустить».
Выберите монету, оставьте режим «Бумага» и нажмите «Запустить».
Откройте график этой монеты: лимитка стоит на 0,5% ниже цены и переезжает за ней.
Запуск появится в списке «Запуски» с зелёной точкой. Чтобы остановить его, откройте запуск и нажмите «Стоп», или «Стоп всё» в верхней панели.
Что здесь происходит:
param(...) наверху объявляет параметры — по ним строится окно запуска;
on_start выполняется один раз при запуске;
this.order — переменная, которая живёт между событиями;
watch(lo, hi) просит разбудить скрипт, когда цена выйдет из коридора;
on_price_exit получает цену, на которой это случилось.
Как алгоритм связан с терминалом
Биржа→Ядро Let's Go→событие→ваш код
ваш код→действие→риск-шлюз→ордер на бумаге или на бирже
ордер→график→панель «Ордера»→журнал запуска
Ваш код работает внутри ядра Let's Go, в своём потоке, рядом с данными биржи. Он не ходит на биржу сам и не опрашивает цену в цикле. Он говорит ядру, чего ждать: «разбуди, когда цена выйдет из коридора», «разбуди на закрытии 5-минутки», «разбуди через минуту». Ядро следит само и будит код, только когда что-то случилось. Поэтому даже сотня запусков не нагружает компьютер.
Что делает код в ответ, — это действия: «поставь лимитку», «перенеси ордер», «закрой позицию». Каждое действие проходит риск-шлюз и выполняется так же, как ваши ручные ордера: с теми же TP и SL.
Где это видно:
График пары. Ордера алгоритма — линии с историей переносов, уровни из level() — пунктирные ступеньки, видно, как они двигались.
Панель «Ордера». Ордера алгоритма подписаны его именем и номером запуска.
Журнал запуска. Строки log, сигналы, отказы и раз в минуту — скорость.
Карточка запуска. Позиция, результат, число ордеров и пробуждений; отказ — красным.
Режимы запуска:
«Бумага» — по умолчанию. Ордера исполняются на бумаге по живым ценам, деньги не участвуют.
«Биржа» — только по явному выбору и только под ключом активного профиля этой биржи.
«Проверка» — всё как в работе, но ордера только пишутся в журнал.
Жизненный цикл
Загрузка. Верх скрипта выполняется один раз: строки param(...) объявляют параметры.
Ожидание цены. Ядро ждёт первую цену пары; свечи таймфреймов, названных в коде, грузятся заранее, чтобы индикаторы были тёплыми.
`on_start()`. Один раз. Здесь ставят первые ордера и говорят, чего ждать.
События. Скрипт спит и просыпается только на своих событиях.
Остановка. Скрипт получает on_stop(), его неисполненные ордера снимаются, открытые позиции остаются под своими TP и SL.
Обычные переменные (let x = 1;) живут, пока работает обработчик. То, что нужно помнить между событиями, кладите в this:
Поле, которого ещё нет, читается как пустое значение (). Проверить так: if this.order == () { ... }.
Один алгоритм можно запустить несколько раз — на разных монетах или с разными параметрами: каждый запуск живёт отдельно, со своим this. При перезапуске Let's Go запуски сами не возобновляются.
События
Обработчик — функция с именем события. Если её нет, скрипт это событие не слышит.
on_start()
Один раз при запуске, когда у пары уже есть цена.
fnon_start() {
log(`старт на ${symbol()} по ${price()}`);
}
on_price_exit(price)
Цена вышла из коридора watch(lo, hi). После этого коридор снят: чтобы ждать снова, вызовите watch ещё раз.
Ордер выставлен, перенесён, снят или отклонён. Если обработчика on_fill нет, исполнения тоже приходят сюда. Когда ордер снят или биржа его отклонила, забудьте его, чтобы поставить новый:
pct(base, p) — цена на p% выше (p меньше нуля — ниже): pct(100, -1) — 99;
change(from, to) — изменение в процентах.
Действия
Ордера:
Функция
Что делает
buy(usd), sell(usd)
рыночный ордер на сумму в USDT
buy_limit(price, usd), sell_limit(…)
лимитный ордер
buy_stop(price, usd), sell_stop(…)
стоп-ордер: срабатывает, когда цена дойдёт
move(id, price)
перенос ордера, сумма в деньгах сохраняется
cancel(id), cancel_all()
снять ордер или все ордера запуска
close()
закрыть позицию запуска по рынку
Каждая функция ордера возвращает его номер. Сохраните его в this, чтобы потом перенести или снять ордер.
TP и SL задаются последним аргументом, в процентах от цены исполнения:
fnon_start() {
let p = price();
buy_limit(pct(p, -1), 20, #{ tp: 1.5, sl: 0.7 });
}
Ноги TP и SL выставляет тот же движок, что и для ручных ордеров. 0 означает «без этой ноги».
Ожидание:
Функция
Что делает
watch(lo, hi)
разбудить, когда цена выйдет из коридора [lo, hi]
unwatch()
перестать следить за коридором
trades(usd)
будить на каждой сделке не меньше usd (0 — на каждой)
candles("5m")
будить на закрытии каждой свечи таймфрейма
every(ms, "имя"), after(ms, "имя")
таймер: повторяющийся или один раз, от 100 мс
stop_timer("имя")
снять таймер
Слова и отметки:
Функция
Что делает
log(текст)
строка в журнал запуска
notify(текст)
сигнал в Telegram (вид «Сигнал алгоритма») и в журнал
level("имя", price)
уровень на графике пары; новое значение — новая ступенька, история видна
clear_levels()
убрать уровни
stop()
остановить запуск
Параметры
param("usd", 20); // имя и значение по умолчаниюparam("offset", 0.5, 0.1, 5); // плюс минимум и максимум
Параметры объявляются в начале скрипта, по одному на строку. Окно запуска показывает поле на каждый параметр, а значения проверяет по минимуму и максимуму. Внутри обработчиков значение читается по имени: param("usd"). Все параметры — числа; для выключателя используйте 0 и 1.
Названия параметров в окне запуска: известные имена (usd, tp, sl, corridor, offset…) подписаны по-человечески, остальные показаны как есть.
Риск-шлюз и лимиты
Каждый ордер алгоритма проходит риск-шлюз. Его пределы задаются в окне запуска:
сумма одного ордера;
число открытых ордеров запуска;
число позиций на весь запуск: с монетами BTC и ETH и пределом 1 позиция будет одна;
убыток с начала запуска, после которого новые входы не ставятся;
число переносов ордеров в минуту;
что делать при нарушении: продолжить без этого ордера или остановить запуск.
Что вы увидите:
До запуска. Окно запуска показывает лимит рядом с суммой ордера. Если сумма больше лимита, это видно сразу красной плашкой, и «Запустить» не нажимается, пока не исправите.
В работе. Отказанный ордер не уходит; запуск горит красным с причиной, например «Отказ: сумма 200 $ больше лимита 100 $», рядом — «Изменить лимит». В журнале одна строка на причину.
Скрипту. Он получает on_order со статусом "rejected" и причиной в o.reason.
Монета из чёрного списка останавливает запуск в любом режиме. «Стоп всё» в верхней панели останавливает все запуски сразу.
Скорость
Алгоритм отвечает на рынок за доли миллисекунды. Чтобы так и оставалось, ядро делает лишнюю работу за скрипт и будит его только по делу:
watch(lo, hi) — ядро сверяет каждую сделку с коридором само, скрипт проснётся один раз, когда цена выйдет;
trades(usd) — в on_trade приходят только сделки не меньше суммы, мелочь отсеивает ядро;
every и after — таймеры ведёт ядро, а не цикл в скрипте.
Что делает обработчик медленнее:
candles(tf, n) и book(n) — каждый вызов копирует свечи или стакан, берите столько, сколько нужно, и не в каждой сделке;
длинные циклы и сборка больших строк и массивов на каждом событии;
log на каждом событии: журнал общий, строки сверх предела отбрасываются.
Каждый запуск измеряет себя сам и раз в минуту пишет в журнал строку «Скорость», её же видно в карточке запуска. Время в микросекундах, медиана и p99 (99 из 100 событий не дольше):
до скрипта — от прихода сделки из сокета биржи до начала обработчика;
скрипт — сам обработчик;
до отправки — от ответа обработчика до момента, когда ордер ушёл: на бумаге — встал в бумажную книгу, на бирже — пошёл в запрос;
ответ биржи — сколько биржа отвечала на ордер; это сеть и сама биржа, а не терминал.
Тест в сборке Let's Go гонит ленту 10 000 сделок в секунду через поток скрипта и падает, если путь от сделки до ответа обработчика дольше миллисекунды у 1 из 100 событий.
Ошибки
В коде. Ошибки разбора и опечатки в именях событий и функций видны в редакторе сразу, до запуска: строка подчёркнута красной волной, внизу — что не так.
В работе. Скрипт работает в песочнице:
нет доступа к файлам, сети и ключам;
нет import и eval;
на одно событие даётся до миллиона операций и четверть секунды времени;
строки, массивы и карты ограничены по размеру.
Если скрипт зациклился, превысил предел или упал с ошибкой, запуск останавливается. В его журнале видна причина с номером строки. Остальные запуски и терминал работают дальше.
Отказы биржи. Ордер, который биржа не приняла, снимается, запуск горит красным с причиной биржи, скрипт получает on_order со статусом "rejected". Частые причины:
«Insufficient balance» — на счёте не хватает денег: на споте нужна валюта котировки (USDT), на фьючерсах — маржа;
сумма меньше минимума биржи — запуск останавливается и пишет, какая сумма нужна; у фьючерсов минимумы больше, BTC — около 100 USDT;
цена за пределами коридора цен биржи — лимитка слишком далеко от рынка;
лимит запросов — ордер уйдёт снова через паузу, отказ не нужно повторять в коде.
Примеры
Шаблоны, с которых начинается новый алгоритм, — это готовые рабочие скрипты. Каждый проверяется тестом на тестовой ленте: «Буфер» должен пропустить медленный дрейф и исполниться на прострел, пробой — войти в сторону выхода, и так далее.
Буфер
коридор и ордер за ним
// Буфер. Вокруг цены коридор, за ним лимитный ордер.// Пока цена в коридоре, ничего не происходит. Цена вышла: коридор// и ордер переезжают к новой цене. Медленный дрейф ордер обходит,// резкий прострел его исполняет.param("side", 1, -1, 1); // 1 — покупка ниже цены, -1 — продажа вышеparam("corridor", 0.2, 0.02, 5); // коридор в каждую сторону, %param("offset", 0.5, 0.05, 10); // ордер от цены, %param("usd", 20, 5, 100000); // сумма ордера, USDTparam("tp", 1, 0, 50); // тейк-профит от входа, % (0 — без него)param("sl", 0.5, 0, 50); // стоп-лосс от входа, % (0 — без него)fnon_start() {
this.recenter();
}
// Цена вышла из коридора: переносим коридор и ордер (или ставим// новый, если прежний сняли). В позиции буфер не нужен.fnon_price_exit(price) {
ifposition().side == "flat" {
this.recenter();
}
}
// Ордер сняли или биржа отказала: на следующем выходе цены встанет новый.fnon_order(o) {
if o.id == this.order && (o.status == "cancelled" || o.status == "rejected") {
this.order = ();
}
}
// Ордер исполнился: дальше позицию ведут TP и SL.fnon_fill(o) {
if o.id == this.order && o.status == "filled" {
log(`вход по ${o.avg}`);
this.order = ();
unwatch();
clear_levels();
}
}
// Позиция закрылась: снова ставим буфер.fnon_position(p) {
if p.side == "flat" && this.order == () {
this.recenter();
}
}
fn recenter() {
let p = price();
let c = param("corridor");
watch(pct(p, -c), pct(p, c));
level("low", pct(p, -c));
level("high", pct(p, c));
letbuy = param("side") >= 0;
let off = param("offset");
let target = ifbuy { pct(p, -off) } else { pct(p, off) };
ifthis.order == () {
let legs = #{ tp: param("tp"), sl: param("sl") };
this.order = ifbuy {
buy_limit(target, param("usd"), legs)
} else {
sell_limit(target, param("usd"), legs)
};
} else {
move(this.order, target);
}
}
Отскок после падения
купить на резком падении и продать на отскоке
// Купить на резком падении и продать на отскоке.// Цена упала на X% от максимума последних T секунд: покупка по рынку.// Тейк продаёт на отскоке, стоп ограничивает убыток. Следующая// покупка не раньше паузы и только без открытой позиции.param("drop", 2, 0.1, 50); // падение от максимума окна, %param("seconds", 300, 10, 3600); // окно, секундparam("usd", 20, 5, 100000); // сумма покупки, USDTparam("tp", 1, 0.1, 50); // отскок, на котором продать, %param("sl", 1, 0.1, 50); // стоп-лосс от входа, %param("pause", 600, 0, 86400); // пауза после покупки, секундfnon_start() {
this.points = [];
this.last = now() - param("pause") * 1000; // первая покупка не ждёт паузыevery(1000, "probe");
}
fnon_timer(name) {
let t = now();
let p = price();
if p <= 0 {
return;
}
this.points.push([t, p]);
whilethis.points.len() > 1 && t - this.points[0][0] > param("seconds") * 1000 {
this.points.remove(0);
}
let top = p;
for x inthis.points {
if x[1] > top {
top = x[1];
}
}
let fell = change(top, p);
if fell <= -param("drop") && position().side == "flat" && t - this.last >= param("pause") * 1000 {
buy(param("usd"), #{ tp: param("tp"), sl: param("sl") });
log(`покупка на падении ${fell.round()}%`);
this.last = t;
this.points = [[t, p]];
}
}
Пробой диапазона
вход на выходе из диапазона
// Пробой диапазона. Максимум и минимум последних N минут задают// коридор. Цена вышла из него: вход по рынку в сторону пробоя.param("minutes", 15, 2, 240); // окно диапазона, минутparam("usd", 20, 5, 100000); // сумма входа, USDTparam("tp", 1, 0, 50); // тейк-профит, %param("sl", 0.5, 0, 50); // стоп-лосс, %fnon_start() {
candles("1m");
this.done = false;
this.arm();
}
// Каждая закрытая минута сдвигает окно.fnon_candle(c) {
if !this.done {
this.arm();
}
}
fnon_price_exit(price) {
ifthis.done || this.hi == () {
return;
}
this.done = true;
let legs = #{ tp: param("tp"), sl: param("sl") };
ifprice > this.hi {
buy(param("usd"), legs);
} else {
sell(param("usd"), legs);
}
notify(`${symbol()}: пробой ${price}`);
clear_levels();
}
fn arm() {
let n = param("minutes").to_int();
let cs = candles("1m", n + 1);
if cs.len() < n + 1 {
return;
}
cs.pop(); // последняя свеча ещё открытаlet hi = cs[0].h;
let lo = cs[0].l;
for c in cs {
if c.h > hi { hi = c.h; }
if c.l < lo { lo = c.l; }
}
this.hi = hi;
this.lo = lo;
watch(lo, hi);
level("high", hi);
level("low", lo);
}
Пересечение EMA
лонг по пересечению средних
// Пересечение EMA на 5-минутках. Быстрая выше медленной:// позиция в лонг; ниже: закрываем (и в шорт, если разрешён).param("fast", 9, 2, 200); // период быстрой EMAparam("slow", 21, 3, 500); // период медленной EMAparam("usd", 20, 5, 100000); // сумма входа, USDTparam("short", 0, 0, 1); // 1 — открывать и шорт (фьючерсы)fnon_start() {
candles("5m");
this.trend = this.side();
}
fnon_candle(c) {
letnow = this.side();
ifnow == 0 || now == this.trend {
this.trend = now;
return;
}
// Тренд сменился.ifthis.trend != 0 {
close();
cancel_all();
}
ifnow > 0 {
buy(param("usd"));
} elseifparam("short") > 0 {
sell(param("usd"));
}
log(`пересечение: ${if now > 0 { "вверх" } else { "вниз" }}`);
this.trend = now;
}
// 1 — быстрая выше медленной, -1 — ниже, 0 — данных ещё мало.fn side() {
let f = ema("5m", param("fast").to_int());
let s = ema("5m", param("slow").to_int());
if f == () || s == () {
return0;
}
if f > s { 1 } else { -1 }
}
Сетка лимиток
лимитки ниже цены с тейком
// Сетка лимиток ниже цены. Каждая берёт свой шаг тейк-профитом.// Когда всё исполнилось и закрылось, сетка встаёт заново от новой цены.param("levels", 3, 1, 10); // ордеров в сеткеparam("step", 0.3, 0.05, 5); // шаг сетки, %param("usd", 20, 5, 100000); // сумма каждого ордера, USDTfnon_start() {
this.place_grid();
every(60000, "grid");
}
fnon_timer(name) {
iforders().len() == 0 && position().side == "flat" {
this.place_grid();
}
}
fn place_grid() {
let p = price();
let step = param("step");
for i in1..=param("levels").to_int() {
let px = pct(p, -step * i);
buy_limit(px, param("usd"), #{ tp: step });
level(`grid ${i}`, px);
}
}
Сигнал о движении
в Telegram при резком движении
// Сигнал в Telegram, если цена прошла X% за T секунд.// Ордеров не ставит.param("percent", 1, 0.1, 50); // порог движения, %param("seconds", 60, 5, 3600); // окно, секундparam("pause", 300, 0, 86400); // тишина после сигнала, секундfnon_start() {
this.points = [];
this.last = now() - param("pause") * 1000; // первый сигнал не ждёт паузыevery(1000, "probe");
}
fnon_timer(name) {
let t = now();
let p = price();
if p <= 0 {
return;
}
this.points.push([t, p]);
let window = param("seconds") * 1000;
whilethis.points.len() > 1 && t - this.points[0][0] > window {
this.points.remove(0);
}
let moved = change(this.points[0][1], p);
if moved.abs() >= param("percent") && t - this.last >= param("pause") * 1000 {
notify(`${symbol()}: ${moved.round()}% за ${param("seconds")} с`);
this.last = t;
this.points = [[t, p]];
}
}
fnon_start() {
candles("15m");
}
fnon_candle(c) {
let r = rsi("15m", 14);
if r == () {
return;
}
if r < 30 && position().side == "flat" {
buy(20, #{ tp: 2, sl: 1 });
log(`RSI ${r.round()}: вход`);
}
}
Прогон на истории
Кнопка «Прогон на истории» в редакторе прогоняет алгоритм в его текущем виде (даже несохранённый) по сделкам из архива за последние 1, 6 или 24 часа:
сделки сжимаются до одного тика на 100 мс: последняя цена, суммарный объём;
свечи таймфреймов, названных в коде, подгружаются заранее, так что индикаторы тёплые с первой минуты;
лимитка исполняется по своей цене, когда цена до неё дошла; стоп и рынок — по цене сделки;
TP и SL работают как ноги;
риск-шлюз действует так же, как в запуске.
Ничего не уходит на биржу и на бумагу. Результат: сколько раз скрипт проснулся, все исполнения по времени, зафиксированный результат, открытая позиция, сигналы, нарушения риска и строки журнала. Уровни из level() рисуются на графике пары ступеньками до следующего прогона. Комиссии и проскальзывание не учитываются: прогон показывает, что алгоритм попросил бы сделать, а не обещает доход.
Язык v3
Новый язык алгоритмов. Алгоритм в нём не опрашивает рынок, а объявляет:
что считать — value: изменение за минуту, объём, средние, EMA, максимум; ядро считает их само, на каждой сделке, по всем монетам;
когда просыпаться — signal: условие из значений, срабатывает, когда стало истинным, а не пока истинно;
в каком он состоянии — state: «ждём», «в позиции», с таймаутом: state armed for 3m -> watching;
что делать — on сигнал while состояние { … }: код пишет только решение.
Настройки окна запуска — setting с видом и единицей: percent, usd, number, side, choice, switch. Проценты пишутся как есть: price - offset%. Длительности — 5s, 3m, 1h.
Редактор уже понимает язык v3 и показывает ошибки со строкой и по-русски. Запуск таких алгоритмов — со следующей версии. «Буфер» на языке v3:
algorithm"Буфер""Лимитка за коридором вокруг цены: медленный дрейф обходит, резкий прострел исполняет."setting side = side ("Сторона", buy)
setting corridor = percent("Коридор", 0.2, 0.02..5)
setting offset = percent("Ордер от цены", 0.5, 0.05..10)
setting amount = usd ("Сумма", 20)
setting take = percent("Тейк", 1)
settingstop = percent("Стоп", 0.5)
var anchor = price// центр коридора; его переносит place()signal left = priceoutside band(anchor, corridor) // цена ушла за коридор вокруг якоряstate waiting
state holding
on start { place() }
on left while waiting { place() }
on filled while waiting { go holding }
on closed while holding { place(); go waiting }
fn place() {
anchor = price// новый центр коридораlet at = if side == buy { price - offset% } else { price + offset% }
my_order.place_or_move(side, amount, at).take(take%).stop(stop%)
}
Все слова языка v3. Справка в редакторе — F1 или Ctrl+клик по слову.
Объявления
Слово
Что значит
algorithm "Name" "What it does"
Первая строка: имя алгоритма и что он делает.
setting key = kind("Title", default, min..max)
Настройка окна запуска: вид, название, значение по умолчанию и границы.
value name = expression
Что считать: ядро пересчитывает значение само на каждой сделке.
var name = expression
Память алгоритма для каждой монеты: задаётся при старте, меняется в обработчиках.
signal name = condition confirmed 5s cooldown 30s
Когда просыпаться: условие срабатывает, когда стало истинным, а не пока истинно.
state name for 3m -> next
Состояние монеты; с таймаутом — переход в другое через заданное время.
on signal while state { … }
Обработчик: что делать по сигналу или событию, в каком состоянии.
every 1m { … }
Обработчик по времени: раз в заданный период.
fn name() { … }
Своя функция, которую вызывают обработчики.
Слова языка
Слово
Что значит
while · on signal while state
Обработчик работает только в этом состоянии.
confirmed 5s
Сигнал срабатывает, только если условие продержалось столько времени.
cooldown 30s
После срабатывания сигнал молчит столько времени.
for · state name for 3m -> next
Таймаут состояния: через сколько перейти и куда.
go state
Перейти в состояние.
and · a and b
И: истинно, когда истинны оба.
or · a or b
Или: истинно, когда истинно хоть одно.
not a
Не: обратное условие.
crosses_up · a crosses_up b
Пересекло снизу вверх: было ниже, стало выше.
crosses_down · a crosses_down b
Пересекло сверху вниз: было выше, стало ниже.
inside · x inside band(center, width)
Значение внутри коридора band.
outside · x outside band(center, width)
Значение вышло за коридор band.
let x = expression
Временная переменная внутри обработчика.
if condition { … } else { … }
Условие внутри обработчика.
return
Закончить обработчик сейчас.
События
Слово
Что значит
start · on start { … }
Монета начала работать в экземпляре.
filled · on filled { … }
Ордер алгоритма исполнился.
closed · on closed { … }
Позиция монеты закрылась.
refused · on refused { … }
Биржа или лимиты отказали в ордере.
stop · on stop { … }
Монета перестаёт работать: остановка, пауза или закрытие терминала.
Функции значений и сигналов
Слово
Что значит
change("1m") → %
Изменение цены за окно, в процентах.
volume("1m") → $
Объём за окно, в долларах.
open("15m")
Цена открытия текущей свечи таймфрейма.
high("15m")
Максимум текущей свечи таймфрейма.
low("15m")
Минимум текущей свечи таймфрейма.
close("15m")
Цена закрытия последней закрытой свечи таймфрейма.
range("5m") → %
Размах цены за окно: от минимума до максимума, в процентах.
highest(x, "5m")
Наибольшее значение за окно.
lowest(x, "5m")
Наименьшее значение за окно.
average(x, n)
Среднее из последних n значений.
ema(x, n)
Экспоненциальная средняя за n значений.
sma(x, n)
Простая средняя за n значений.
rsi(x, n) → 0..100
Индекс относительной силы за n значений, от 0 до 100.
band(center, width%)
Коридор вокруг центра заданной ширины, для inside и outside.
abs(x)
Значение без знака.
min(a, b)
Меньшее из двух.
max(a, b)
Большее из двух.
metric("natr")
Колонка Market Live по её имени: natr, rd5m и другие.
Экземпляр
Слово
Что значит
rank · instance.rank(value) → 1, 2, …
Место монеты среди монет экземпляра по значению: 1 — самая сильная.
in_position · instance.in_position → n
Сколько монет экземпляра сейчас в позиции.
Что даёт ядро
Слово
Что значит
price
Последняя цена монеты.
bid
Лучшая цена покупки в стакане.
ask
Лучшая цена продажи в стакане.
mark
Маркировочная цена фьючерса.
instance.rank(x), instance.in_position
Экземпляр алгоритма: все его монеты вместе.
coin
Монета, на которой работает обработчик.
Виды настроек
Слово
Что значит
number("Title", 3, 1..10)
Число с границами.
percent("Title", 0.5, 0.05..10)
Проценты: в коде пишется как offset%.
usd("Title", 20)
Сумма в долларах.
switch("Title", true)
Да или нет.
choice("Title", a, [a: "A", b: "B"])
Один из своих вариантов.
side("Title", buy)
Покупка или продажа: buy или sell.
timeframe("Title", "5m")
Таймфрейм: 15s, 1m, 5m, 1h и другие.
order_type("Title", limit)
Тип ордера: limit, market или stop.
price_source("Title", last)
Откуда брать цену: last, mark, bid или ask.
coin("Title")
Монета из списка пар.
drawing("Title", hline)
Рисунок на графике как вход: линия, луч, канал, зона, уровни Фибоначчи.
Единицы
Слово
Что значит
% · price - offset%
Процент от того, к чему прибавляется или из чего вычитается: price - 1% — на процент ниже цены.
ms · 250ms
Миллисекунды.
s · 5s
Секунды.
m · 3m
Минуты.
h · 1h
Часы.
d · 1d
Дни.
Справочник
Всё, что можно вызвать из скрипта, и все события. Список собирается из ядра: если функция есть здесь, она есть в движке.
События
Функция
Что делает
on_start()
Один раз при запуске, когда у пары есть цена.
on_price_exit(price)
Цена вышла из коридора watch(lo, hi); price — цена выхода.
on_trade(t)
Сделка не меньше суммы из trades(usd): t.price, t.qty, t.usd, t.side, t.t.