05k.ru
← к скриптам

Скрипты QUIK — документация SDK

Скрипты пишутся на Lua 5.4 поверх нашего SDK и исполняются в вашем терминале QUIK. Вы пишете только логику через готовые функции — подключение, ACCOUNT и доступ к QUIK мы берём на себя. Текущая версия — мониторинг (только чтение): данные, события, вывод. Торговых функций пока нет.

Как это работает

  1. В ЛК → Скрипты создаёте скрипт, привязываете к подключению QUIK, пишете код и жмёте Сохранить.
  2. Запустить — код уходит в ваш шлюзовой скрипт QUIK, компилируется и начинает работать (вызываются ваши обработчики). Остановить — выгружает.
  3. Вывод alert(...) появляется в журнале под редактором. Статус (работает / ошибка / остановлен) виден там же.
  4. Если перезапустить QUIK — запущенные скрипты поднимутся автоматически при переподключении шлюза.

Требование: шлюзовой скрипт подключения должен быть запущен в QUIK (Lua 5.4, рядом обе dll).

Ограничения и песочница

  • Пишите через обработчики (on_timer и т.п.), а не свой бесконечный цикл: все скрипты работают в одном потоке шлюза, и while true do … end его подвесит.
  • Доступен только SDK и безопасная часть стандартной Lua. Недоступны сырой QUIK API (sendTransaction, getItem…), файлы (io), запуск процессов (os.execute).
  • Только чтение — заявки из скрипта пока выставлять нельзя (появится в следующей версии, под торговым PIN и лимитами).
  • Ошибка компиляции или выполнения показывается статусом «ошибка» с текстом.

Данные

price(sec [, class]) → number

Последняя цена инструмента sec (тикер). Класс определяется автоматически; можно указать явно ("TQBR", "SPBFUT"…). Вернёт 0, если цены ещё нет.

local p = price("GAZP")
log("Газпром: " .. p)
positions() → таблица позиций

Открытые позиции по всем счетам. Каждый элемент:

{ symbol="SBER", name="Сбербанк", classcode="TQBR",
  qty=10, avg_price=286.5, last=287.1, kind="stock", account="L01-..." }
-- account — торговый счёт (trdacc); у фьючерсов дополнительно: varmargin, kind="futures"
for _, p in ipairs(positions()) do
  log(p.symbol .. ": " .. p.qty .. " шт, ср.цена " .. p.avg_price)
end
money([kind]) → таблица денег

Деньги по счетам. kind"stock" или "futures" (без аргумента — все). Форма зависит от рынка:

-- фондовый:  { kind="stock",   currency="SUR", balance=..., locked=..., account="..." }
-- срочный:   { kind="futures", currency="SUR", free=..., go=..., varmargin=..., account="..." }
--   free — свободно, go — ГО, всего = free + go; account — торговый счёт (trdacc)
for _, m in ipairs(money("futures")) do
  if m.free < 5000 then alert("Мало свободных: " .. m.free, "warn") end
end
orders() → таблица активных заявок

Активные (не исполненные, не снятые) заявки. Каждый элемент:

{ symbol="SBER", classcode="TQBR", side="B",   -- "B" покупка, "S" продажа
  price=280, qty=10, balance=10, order_num="123456789" }

События

Регистрируются один раз (обычно в начале скрипта). Обработчик вызывается при наступлении события.

on_timer(ms, fn)

Вызывать fn() каждые ms миллисекунд.

on_timer(10000, function()
  log("тик каждые 10 секунд")
end)
on_price(sec, ms, fn)

Каждые ms мс вызывать fn(price) с последней ценой инструмента.

on_price("SBER", 5000, function(p)
  if p > 300 then alert("SBER выше 300: " .. p) end
end)
on_order(fn)

fn(order) при любом изменении заявки. order = { symbol, classcode, side, price, qty, balance, active } (active — активна ли ещё).

on_order(function(o)
  if not o.active then log("заявка закрыта: " .. o.symbol) end
end)
on_trade(fn)

fn(trade) при своей сделке. trade = { symbol, price, qty, is_buy, classcode } (is_buy — покупка ли).

on_trade(function(t)
  alert("Сделка: " .. t.symbol .. " " .. t.qty .. " @ " .. t.price)
end)

Вывод

alert(msg [, level])

Сообщение в журнал личного кабинета (видно в вебе). level"info" (по умолч.), "warn", "error".

alert("Внимание: просадка", "warn")
log(msg)

Сообщение в окно отладки самого QUIK (в вебе не видно). Удобно для локальной отладки.

ACCOUNT

Константа — латинское имя подключения, к которому привязан скрипт.

Хранилища позиций

Хранилище — именованный журнал позиций (ЛК → Хранилища), независимый от подключения. Робот записывает в него сделки, а система ведёт стек открытий по каждому инструменту (по каким ценам открыли), закрытия и итоговый PnL. Хранилище нужно заранее создать в ЛК — его латинское имя робот и указывает в вызове.

store.fill(name, t)

Записать сделку в хранилище name. Поля таблицы t:

  • sec — тикер инструмента (обязательно).
  • op"B" покупка / "S" продажа (или buy=true/false).
  • qty — объём в лотах, > 0 (обязательно).
  • price — цена сделки (нужна для расчёта PnL при закрытии).
  • class — класс инструмента (необязательно; определится сам).
  • price_step, step_price — шаг цены и его стоимость (для PnL фьючерсов; подтянутся сами, если не заданы).
  • writeofftrue для списания (см. ниже).
-- открыть/долить: копит стек открытий
store.fill("scalper", { sec="GAZP", op="B", price=145.20, qty=10 })

-- закрыть (конверсия): считает PnL и пишет в журнал закрытий
store.fill("scalper", { sec="GAZP", op="S", price=146.00, qty=10 })
Два вида закрытия
  • Конверсия (по умолчанию) — закрытие с расчётом прибыли; запись попадает в журнал закрытий и в итоговый PnL.
  • Списание (writeoff=true) — просто снимает лоты без расчёта PnL. Нужно для ручного переноса позиции (например, с фьючерса на спот), когда прибыль считать не надо.

Открытие (набор позиции) — всегда конверсия; на него writeoff не влияет.

-- списать 5 лотов без расчёта прибыли
store.fill("carry", { sec="SiH6", op="S", qty=5, writeoff=true })
Пример: писать свои сделки в хранилище
on_trade(function(t)
  store.fill("scalper", { sec=t.symbol, buy=t.is_buy,
                          price=t.price, qty=t.qty })
end)

Модули

Общий код можно вынести в модуль (ЛК → Модули) и подключать в скриптах через require("имя"). Модуль — это Lua-файл, который возвращает таблицу функций. Модули общие для всех ваших скриптов и не привязаны к подключению. Имя латинское — им и подключается модуль.

require(name) → значение модуля

Загружает модуль name и возвращает то, что модуль вернул (обычно таблицу). Модулю доступен тот же SDK (price, positions, money, store…). Результат кэшируется на время работы скрипта; модуль может require другие модули (циклы запрещены).

Модуль risk:
local M = {}

function M.low_free(limit)
  for _, m in ipairs(money("futures")) do
    if (m.free or 0) < limit then return true end
  end
  return false
end

return M
Скрипт, который его использует:
local risk = require("risk")

on_timer(15000, function()
  if risk.low_free(5000) then
    alert("Мало свободных средств на срочном!", "warn")
  end
end)
Публикация и подключение чужих модулей

Модуль можно опубликовать — тогда он доступен другим пользователям в каталоге и подключается по глобальному имени require("префикс.имя").

  • Публиковать может пользователь с ролью автор (выдаёт администратор) и заданным префиксом в профиле (3–10 латинских букв/цифр, уникален).
  • Свои приватные модули подключаются по короткому имени (require("risk")), опубликованные чужие — по полному (require("acme.risk")).
  • Внутри опубликованного модуля другие опубликованные модули тоже подключайте по полному имени (prefix.name).
local risk = require("acme.risk")   -- чужой опубликованный модуль
local util = require("util")        -- свой приватный модуль

Изменили модуль — перезапустите скрипты, которые его подключают: код модулей уходит в шлюз в момент запуска скрипта.

Доступная часть Lua

Стандартные модули и функции, доступные в скрипте:

math, string, table, pairs, ipairs, next, select, tostring, tonumber, type, error, assert, pcall, os.time, os.clock, os.date

Примеры

Цена Газпрома каждые 10 секунд
on_timer(10000, function()
  alert("GAZP: " .. price("GAZP"))
end)
Сигнал при пробое уровня
local level, fired = 300, false
on_price("SBER", 3000, function(p)
  if p >= level and not fired then
    alert("SBER пробил " .. level .. ": " .. p, "warn")
    fired = true
  elseif p < level then
    fired = false
  end
end)
Контроль ГО на срочном счёте
on_timer(15000, function()
  for _, m in ipairs(money("futures")) do
    local total = (m.free or 0) + (m.go or 0)
    if total > 0 and (m.free / total) < 0.2 then
      alert("Свободно меньше 20% ГО: " .. m.free, "warn")
    end
  end
end)
Сводка по портфелю раз в минуту
on_timer(60000, function()
  local n = 0
  for _, p in ipairs(positions()) do n = n + 1 end
  alert("Позиций в портфеле: " .. n)
end)