Использование мессенджера MAX для обмена данными в QUIK, Использование мессенджер для обмена для отправки сообщений и управление торговым приводом в QUIK
Пользователь
Сообщений: Регистрация: 08.05.2020
16.09.2026 08:44:38
Поскольку , эта тема вроде нигде не поднималась , опишу свой опыт , может быть будет кому то полезно. Для информирования и контроля за работой торгового привода у меня был поднят небольшой саит на локальном компе с QUIK ,и меня все устраивало, Сейчас эта схема практически утратила смысл из за почти постоянного отключения мобильного интернета. Я решил посмотреть , что можно использовать взамен. Нормально работали только саиты из белого списка и MAX. Поскольку других вариантов не было , выбор пал на MAX , И так , что же мог предложить MAX для решения этой задачи ? В MAX можно создавать чат боты , но делать это могут только OOO ,ИП и самозанятые. Я давно имею статус самозанятого , а для тех кто его не имеет , получить его можно онлаин за 5 минут и этот статус ни к чему не обязывает. Для самозанятых MAx ставит ограничение в 2 чат бота. Мне хватило 1 бота, и 1 канала. Я зашел в лк MAX ( ) как самозанятый и зарегил бота , создал там же канал. Далее чат бота я сделал администратором канала. В чат чат- бота я направил информационные сообщения от робота а в канале закрепил набор кнопок для отправки управляющих команд для торгового привода. Правда пришлось помучиться с вычислением идентификаторов каналов chat_id. Проще всего это сделать запустив web версию и взять эти id из URL (у чат бота это положительное число в конце URL , а у канала отрицательное).
Привожу пример отправки сообщения в чат бот Max. Для отправки HTTP сообщения я использовал c библиотеку luacom.dll +WinHttp.WinHttpRequest.5.1, но есть и много других способов (напимер curl)
Код
-----------------------------------
function send_max(msg,chat_id,access_token,nFormat)
-----------------------------------
---грузим luacom
luacom = require("luacom")
--для формата markdown
-- # Формат markdown \n **жирный**, _курсив_, ~~зачёркнутый~~, ++подчёркнутый++, `моноширинный` , ^^Выделенный^^ , [Документация](https://dev.max.ru/)
local url,status,responseText,cUser,body,cFormat,http
--chat_id =117.......474
--access_token = "f9LHodD0cOIq...................0BhMw0gm_6f1-DBY"
if not nFormat then
nFormat=0
end
---выбор формата разметки
if nFormat==0 then
--без формата
cFormat=''
elseif nFormat==1 then
--HTML
cFormat= ',"format": "html"'
elseif nFormat==2 then
--markdown
cFormat= ',"format": "markdown"'
end
-- URL
url = "https://platform-api2.max.ru/messages?" .. "chat_id=" .. chat_id
--Тело запроса (JSON)
body ='{"text":"' .. msg .. '"'..cFormat..'}'
-- Создаём COM-объект
http = luacom.CreateObject("WinHttp.WinHttpRequest.5.1")
if http then
-- Открываем соединение (синхронно)
http:Open("POST", url, false)
-- Заголовки
http:SetRequestHeader("Authorization", access_token)
http:SetRequestHeader("Content-Type", "application/json")
http:SetRequestHeader("Accept","application/json")
--Отправляем
http:Send(body)
--Получаем ответ
status = http.Status
responseText = http.ResponseText
if http.Status==200 then
---ok
else
message("Status: " .. tostring(status), 3)
message("Response: " .. tostring(responseText), 3)
end
-- Освобождаем объект
http = nil
else
message("Не удалось создать WinHttp.WinHttpRequest.5.1", 3)
end
end
Пользователь
Сообщений: Регистрация: 15.06.2023
16.09.2026 16:52:24
Спасибо,Евгений, для тех кто не в теме и для меня лично, поясните, а зачем все эти премудрости? Что это дает? и А каком приводе идет речь, типа Бондаря для скальпинга?
Пользователь
Сообщений: Регистрация: 08.05.2020
16.09.2026 17:24:12
Цитата
VPM написал: Спасибо,, для тех кто не в теме и для меня лично, поясните, а зачем все эти премудрости? Что это дает? и А каком приводе идет речь, типа Бондаря для скальпинга?
Вы же не можете сидеть безвылазно перед компьютером с quik ом .Нужно отлучаться по делам или на работу или на отдых. И хочется контролировать работу робота , иногда поменять стратегию , или вообще остановить в случае форс мажора или поднастроить немного. Посмотреть отчет по сделкам , по состоянию депо и.т.п. Для таких же целей банки делают свои приложения на смартфоны . А привод в данной теме речи не играет. У меня своя самомиска для торговли на спредах на облигациях.
Цитата
Пользователь
Сообщений: Регистрация: 15.06.2023
16.09.2026 17:31:47
То есть если я правильно понимаю, контроль за роботом (Вашей самопиской) в окне МАХ? А во сколько можно оценить потерю во времени прохождения команды? Ну хотя бы примерно ведь наверно задавались данным вопросом. Если можно продемонстрируйте картинкой.
Пользователь
Сообщений: Регистрация: 08.05.2020
16.09.2026 18:13:46
Цитата
VPM написал: То есть если я правильно понимаю, контроль за роботом (Вашей самопиской) в окне МАХ? А во сколько можно оценить потерю во времени прохождения команды? Ну хотя бы примерно ведь наверно задавались данным вопросом. Если можно продемонстрируйте картинкой.
У ботов от макс есть 2 варианта обмена основной - через Webhook и Long Polling . Long Polling - это что то типа временной заглушки для тестирования. Я использую Long Polling . В цикле MAIN quik а раз в 5 секунд выполняю опрос бота , разбираю ответ и выполняю принятые команды и отсылаю результат в чат , если требуется. Получается на ответ от 6 до 7 секунд.Через Webhook можно сделать быстрее , но там надо намного больше заморачиваться. Читайте , если есть желание разобраться подробнее.
Еще раз благодарю Вас за пример и просвещение. Меня собственно заинтересовало, возможность получить дополнительный вывод. Для не большого скрипта - стохастический генератор Monte Carlo generator (WinRate + ProfitFactor Lua 5.4). Используемый для проверки гипотезы. Но мне нужен переход от генератора результатов к симулятору позиции. Отличие принципиальное: Mодель генератора: Trade -> Result Новая: State(t) -> Price \ Path -> Position -> Exit -> Capital. То есть сделка становится процессом, а не точкой. Ну и соответственно вывод со сменой входящих параметров. Надеюсь не запутал? Подходит под подобную задачу?
Пользователь
Сообщений: Регистрация: 30.01.2015
18.09.2026 09:31:48
Цитата
Евгений написал: Поскольку , эта тема вроде нигде не поднималась , опишу свой опыт , может быть будет кому то полезно. Для информирования и контроля за работой торгового привода у меня был поднят небольшой саит на локальном компе с QUIK ,и меня все устраивало, Сейчас эта схема практически утратила смысл из за почти постоянного отключения мобильного интернета. Я решил посмотреть , что можно использовать взамен. Нормально работали только саиты из белого списка и MAX. Поскольку других вариантов не было , выбор пал на MAX ,
Код
Странно, если мобильный интернет не работает то и МАХ не должен работать. скорее всего не работал DNS cервер, поэтому не могли найти IP адрес ---------------------------- из моего опыта . Кратко. можно не делать сайт , а создать сервер на локальном компьютере там же где квик Потом сообщить на смартфон IP адрес своего локального компа , у которого есть выход в интернет. Будете работать напрямую со своим сервером. ------------------------------ Еще варианты прокси или VPN можно бесплатные
написал: Поскольку , эта тема вроде нигде не поднималась , опишу свой опыт , может быть будет кому то полезно. Для информирования и контроля за работой торгового привода у меня был поднят небольшой саит на локальном компе с QUIK ,и меня все устраивало, Сейчас эта схема практически утратила смысл из за почти постоянного отключения мобильного интернета. Я решил посмотреть , что можно использовать взамен. Нормально работали только саиты из белого списка и MAX. Поскольку других вариантов не было , выбор пал на MAX ,
Код
Странно, если мобильный интернет не работает то и МАХ не должен работать. скорее всего не работал DNS cервер, поэтому не могли найти IP адрес ---------------------------- из моего опыта . Кратко. можно не делать сайт , а создать сервер на локальном компьютере там же где квик Потом сообщить на смартфон IP адрес своего локального компа , у которого есть выход в интернет. Будете работать напрямую со своим сервером. ------------------------------ Еще варианты прокси или VPN можно бесплатные
Мобильный интернет то работает , только ничего кроме max , приложений некоторых банков и мир pay не открывается. Когда объявлена беспилотная опасность , то все кроме белого списка блокируется наглухо. А у нас в краснодарском крае эта опасность объявлена почти постоянно .Может на счет сайта я не совсем точно выразился . У меня работает локально на компе с квиком web сервис +корпоративный open vpn , так как выделено ip у меня нет .
Пользователь
Сообщений: Регистрация: 30.01.2015
20.09.2026 07:27:51
Еще один вариант. Особо не отлаживал. Выкладываю как есть:
Код
-- max_bridge.lua
-- Двусторонний мост QUIK <-> MAX Messenger
-- Lua 5.3/5.4: native bitwise, //, math.tointeger, <const>
-- Очереди: T={{},0,0} — массив + jw/jr, сброс в push
local MAX = {}
--========== КОНФИГУРАЦИЯ ==========--
MAX.TOKEN = "ВАШ_ТОКЕН"
MAX.API_URL = "https://platform-api2.max.ru"
MAX.CHAT_ID = nil
MAX.LAST_MARKER = 0
MAX.LAST_SEND = 0.0
MAX.LAST_POLL = 0.0
MAX.ERR_COUNT = 0
MAX.HTTP = nil
local luacom_ok, luacom = pcall(require, "luacom")
local MSG_GAP <const> = 2.0
local POLL_GAP <const> = 2.0
local ONIDLE_RET <const> = 200
local F_ACTIVE <const> = 1
local F_SELL <const> = 4
--========== ОЧЕРЕДИ ==========--
-- T = { buf, jr, jw }
-- T[1] — массив данных
-- T[2] — указатель чтения (jr)
-- T[3] — указатель записи (jw)
-- При jr == jw (очередь пуста) — сброс в 0, следующая запись в buf[1]
local function setQ(T, X)
local jw = T[3]
if T[2] == jw then
jw = 0
T[2] = 0
end
jw = jw + 1
T[1][jw] = X
T[3] = jw
end
local function getQ(T)
local jr = T[2]
if T[3] > jr then
jr = jr + 1
T[2] = jr
return T[1][jr]
end
end
local function qsize(T)
return T[3] - T[2]
end
MAX.INCOMING = {{}, 0, 0}
MAX.OUTGOING = {{}, 0, 0}
--========== ТАБЛИЦЫ КОДИРОВАНИЯ/ДЕКОДИРОВАНИЯ ==========--
local escape_map <const> = {
[92] = '\\\\', -- \ > \\
[34] = '\\"', -- " > \"
[10] = '\\n', -- \n > \n
[13] = '\\r', -- \r > \r
[9] = '\\t', -- \t > \t
}
local unescape_map <const> = {
[110] = '\n', -- n > \n
[114] = '\r', -- r > \r
[116] = '\t', -- t > \t
[34] = '"', -- " > "
[92] = '\\', -- \ > \
[47] = '/', -- / > /
}
--========== КОДИРОВАНИЕ (escape) ==========--
local function json_escape(s)
local res, j = {}, 0
for i = 1, #s do
local b = s:byte(i)
local rep = escape_map[b]
if rep then
j = j + 1
res[j] = rep
else
j = j + 1
res[j] = s:sub(i, i)
end
end
return table.concat(res)
end
--========== ДЕКОДИРОВАНИЕ (JSON-парсер) ==========--
local J = { s = "", p = 1 }
local j_skip, j_val
j_skip = function()
while J.p <= #J.s do
local c = J.s:byte(J.p)
if c == 32 or c == 9 or c == 10 or c == 13 then J.p = J.p + 1
else break end
end
end
j_val = function()
j_skip()
local c = J.s:byte(J.p)
if c == 34 then
J.p = J.p + 1
local buf, i = {}, 0
while J.p <= #J.s do
local ch = J.s:byte(J.p)
if ch == 92 then
local n = J.s:byte(J.p + 1)
local rep = unescape_map[n]
if rep then
buf[i + 1] = rep
else
buf[i + 1] = J.s:sub(J.p + 1, J.p + 1)
end
i = i + 1
J.p = J.p + 2
elseif ch == 34 then
J.p = J.p + 1
return table.concat(buf)
else
buf[i + 1] = J.s:sub(J.p, J.p)
i = i + 1
J.p = J.p + 1
end
end
return table.concat(buf)
elseif c == 123 then
J.p = J.p + 1; j_skip()
local obj = {}
if J.s:byte(J.p) == 125 then J.p = J.p + 1 return obj end
while true do
j_skip()
local key = j_val()
j_skip()
if J.s:byte(J.p) == 58 then J.p = J.p + 1 end
obj[key] = j_val()
j_skip()
local sep = J.s:byte(J.p)
if sep == 44 then J.p = J.p + 1
elseif sep == 125 then J.p = J.p + 1 break
else break end
end
return obj
elseif c == 91 then
J.p = J.p + 1; j_skip()
local arr = {}
if J.s:byte(J.p) == 93 then J.p = J.p + 1 return arr end
while true do
arr[#arr + 1] = j_val()
j_skip()
local sep = J.s:byte(J.p)
if sep == 44 then J.p = J.p + 1
elseif sep == 93 then J.p = J.p + 1 break
else break end
end
return arr
else
local start = J.p
while J.p <= #J.s do
local b = J.s:byte(J.p)
if b == 32 or b == 9 or b == 10 or b == 13
or b == 44 or b == 125 or b == 93 then break end
J.p = J.p + 1
end
local v = J.s:sub(start, J.p - 1)
if v == "true" then return true
elseif v == "false" then return false
elseif v == "null" then return nil
else return tonumber(v) or v end
end
end
local function json_parse(str)
J.s = str; J.p = 1
return j_val()
end
--========== HTTP ==========--
local function http(method, url, body)
if not MAX.HTTP then return nil, 0 end
MAX.HTTP:Open(method, url, false)
MAX.HTTP:SetRequestHeader("Authorization", MAX.TOKEN)
MAX.HTTP:SetRequestHeader("Accept", "application/json")
if body then
MAX.HTTP:SetRequestHeader("Content-Type", "application/json")
MAX.HTTP:Send(body)
else
MAX.HTTP:Send()
end
return MAX.HTTP.ResponseText, MAX.HTTP.Status
end
local function raw_send(msg, chat_id, nFormat)
chat_id = chat_id or MAX.CHAT_ID
if not chat_id then return false end
chat_id = math.tointeger(chat_id) or chat_id
local fmt = ""
if nFormat == 1 then fmt = ',"format":"html"'
elseif nFormat == 2 then fmt = ',"format":"markdown"' end
local body = '{"text":"' .. json_escape(msg) .. '"' .. fmt .. '}'
local url = MAX.API_URL .. "/messages?chat_id=" .. tostring(chat_id)
local resp, status = http("POST", url, body)
if status == 200 then
MAX.ERR_COUNT = 0
return true
end
MAX.ERR_COUNT = MAX.ERR_COUNT + 1
if MAX.ERR_COUNT <= 3 then
message("MAX: " .. tostring(status) .. " " .. tostring(resp):sub(1, 150), 3)
end
return false
end
--========== ПУБЛИЧНЫЙ API ==========--
function SendToMAX(msg, chat_id, nFormat)
setQ(MAX.OUTGOING, { m = msg, c = chat_id, f = nFormat or 0 })
end
--========== POLLING ==========--
local function poll()
local url = MAX.API_URL .. "/updates?marker=" .. tostring(MAX.LAST_MARKER)
.. "&timeout=0"
local resp, status = http("GET", url, nil)
if status ~= 200 or not resp then
MAX.ERR_COUNT = MAX.ERR_COUNT + 1
return
end
MAX.ERR_COUNT = 0
local data = json_parse(resp)
if not data then return end
if data.marker then MAX.LAST_MARKER = data.marker end
if not data.updates then return end
for _, upd in ipairs(data.updates) do
local utype = upd.update_type
if utype == "bot_started" then
if upd.chat and upd.chat.chat_id then
MAX.CHAT_ID = math.tointeger(upd.chat.chat_id) or upd.chat.chat_id
end
message("MAX: chat_id = " .. tostring(MAX.CHAT_ID))
SendToMAX("? Связь установлена. /help — список команд", nil, 2)
elseif utype == "message_created" then
local msg = upd.message
if not msg then goto next_upd end
if msg.recipient and msg.recipient.chat_id then
MAX.CHAT_ID = math.tointeger(msg.recipient.chat_id) or msg.recipient.chat_id
end
local text = msg.body and msg.body.text or ""
setQ(MAX.INCOMING, text)
message("MAX < " .. text)
end
::next_upd::
end
end
--========== ДИСПЕТЧЕР ==========--
local function dispatch()
local now = os.clock()
if now - MAX.LAST_SEND < MSG_GAP then return end
-- Приоритет: исходящие
if qsize(MAX.OUTGOING) > 0 then
local item = getQ(MAX.OUTGOING)
if raw_send(item.m, item.c, item.f) then
MAX.LAST_SEND = os.clock()
end
return
end
-- Затем: подтверждение входящего
if qsize(MAX.INCOMING) > 0 then
local text = getQ(MAX.INCOMING)
local reply = MAX.build_reply(text)
if raw_send(reply, nil, 2) then
MAX.LAST_SEND = os.clock()
end
return
end
end
--========== ОБРАБОТКА КОМАНД ==========--
function MAX.build_reply(text)
if not text then return "/help — список команд" end
local cmd = text:lower():gsub("^%s+", ""):gsub("%s+$", "")
if cmd == "/help" or cmd == "помощь" then
return "Команды:\n/status — состояние\n/positions — позиции\n/orders — заявки\n/trades — последние сделки\n/help — список"
elseif cmd == "/status" or cmd == "статус" then
return "QUIK работает. " .. os.date("%Y-%m-%d %H:%M:%S")
elseif cmd == "/positions" or cmd == "позиции" then
local lines, n = {}, 0
for i = 0, getNumberOf("FUTURES_CLIENT_HOLDING") - 1 do
local p = getItem("FUTURES_CLIENT_HOLDING", i)
if p and p.openqty ~= 0 then
n = n + 1
local qty = math.abs(math.tointeger(p.openqty) or tonumber(p.openqty) or 0)
local vm = tonumber(p.varmargin) or 0
lines[n] = string.format("%s **%s** %d шт, вм %.2f",
p.openqty > 0 and "Лонг" or "Шорт",
tostring(p.sec_code), qty, vm)
end
end
return n == 0 and "Нет позиций." or table.concat(lines, "\n")
elseif cmd == "/orders" or cmd == "заявки" then
local lines, n = {}, 0
for i = 0, getNumberOf("ORDERS") - 1 do
local o = getItem("ORDERS", i)
if o and (o.flags & F_ACTIVE) ~= 0 then
n = n + 1
local qty = math.tointeger(o.qty) or tonumber(o.qty) or 0
local price = tonumber(o.price) or 0
lines[n] = string.format("%s **%s** %d @ %.2f",
(o.flags & F_SELL) ~= 0 and "Прод" or "Покуп",
tostring(o.sec_code), qty, price)
end
end
return n == 0 and "Нет заявок." or table.concat(lines, "\n")
elseif cmd == "/trades" or cmd == "сделки" then
local lines = {}
local total = getNumberOf("TRADES")
local start = total > 5 and total - 5 or 0
for i = start, total - 1 do
local t = getItem("TRADES", i)
if t then
local qty = math.tointeger(t.qty) or tonumber(t.qty) or 0
local price = tonumber(t.price) or 0
lines[#lines + 1] = string.format("%s **%s** %d @ %.2f",
(t.flags & F_SELL) ~= 0 and "Прод" or "Покуп",
tostring(t.sec_code), qty, price)
end
end
return #lines == 0 and "Сделок нет."
or "Последние:\n" .. table.concat(lines, "\n")
else
return "Получено: " .. text .. "\n/help — список команд"
end
end
--========== CALLBACKS QUIK ==========--
function OnInit()
if not luacom_ok then
message("MAX Bridge: luacom не найден!", 3)
return
end
MAX.HTTP = luacom.CreateObject("WinHttp.WinHttpRequest.5.1")
if MAX.HTTP then
MAX.HTTP:SetTimeouts(3000, 3000, 5000, 5000)
message("MAX Bridge: запущен. Lua " .. _VERSION .. ". Ожидание /start...")
else
message("MAX Bridge: WinHttpRequest не создан!", 3)
end
end
function OnIdle()
if not MAX.HTTP then return 1000 end
local now = os.clock()
local interval = POLL_GAP
if MAX.ERR_COUNT > 3 then
interval = POLL_GAP * (MAX.ERR_COUNT < 10 and MAX.ERR_COUNT or 10)
end
if now - MAX.LAST_POLL >= interval then
MAX.LAST_POLL = now
poll()
end
dispatch()
return ONIDLE_RET
end
function OnStop()
local out_n = qsize(MAX.OUTGOING)
local inc_n = qsize(MAX.INCOMING)
if MAX.CHAT_ID then
if out_n + inc_n > 0 then
raw_send(string.format(
"?? MAX Bridge остановлен. В очередях: %d исх, %d вх.",
out_n, inc_n), nil, 0)
else
raw_send("?? MAX Bridge остановлен.", nil, 0)
end
end
MAX.HTTP = nil
return 1000
end
function OnTrade(trade)
local is_sell = (trade.flags & F_SELL) ~= 0
local qty = math.tointeger(trade.qty) or tonumber(trade.qty) or 0
local price = tonumber(trade.price) or 0
SendToMAX(string.format("?? %s **%s** %d @ %.2f",
is_sell and "Прод" or "Покуп",
tostring(trade.sec_code), qty, price), nil, 2)
end
--[[
Инструкция: мост QUIK - MAX
Шаг 1. Создание бота в MAX
Откройте MAX на смартфоне, найдите бота Masterbot (или через бизнес-кабинет , если есть юрлицо/ИП).
Создайте нового бота, получите токен — длинная строка вида f9LHodD0cOIq...0BhMw0gm_6f1-DBY.
Напишите боту команду /start — это нужно, чтобы бот «увидел» вас и скрипт позже сможет подхватить ваш chat_id.
Шаг 2. Установка скрипта в QUIK
Откройте скрипт в любом текстовом редакторе (Блокнот, Notepad++).
Найдите строку MAX.TOKEN = "ВАШ_ТОКЕН" и вставьте свой токен.
Сохраните файл, например, max_bridge.lua.
В QUIK: меню Сервис > Lua-скрипты (или Ctrl+I).
Нажмите Добавить, выберите max_bridge.lua.
Нажмите Запустить.
В окне сообщений QUIK должно появиться: MAX Bridge: запущен. Lua 5.x. Ожидание /start...
Шаг 3. Подключение смартфона
Откройте MAX на телефоне, найдите своего бота.
Отправьте ему команду /start.
Скрипт перехватит событие bot_started, запомнит ваш chat_id и пришлёт ответ: ? Связь установлена. /help — список команд.
Всё — двусторонняя связь работает.
Шаг 4. Команды с телефона
Отправляйте боту в MAX:
Команда Что делает
/help Список всех команд
/status Состояние QUIK, дата/время
/positions Открытые фьючерсные позиции
/orders Активные заявки
/trades Последние 5 сделок
Шаг 5. Отправка сообщений из QUIK
Скрипт регистрирует глобальную функцию SendToMAX().
Любой другой Lua-скрипт в QUIK может вызывать её для отправки уведомлений на телефон:
lua
-- Простой текст (без разметки)
SendToMAX("Сигнал: RTS пробил 100000")
-- Текст с разметкой Markdown (формат 2)
SendToMAX("**Внимание!** Цена `GAZP` упала ниже 200", nil, 2)
-- Текст с разметкой HTML (формат 1)
SendToMAX("<b>Внимание!</b> Цена GAZP упала ниже 200", nil, 1)
-- Отправка в конкретный чат (если chat_id известен)
SendToMAX("Личное сообщение", 117000000474, 0)
Форматы:
0 — без разметки (по умолчанию)
1 — HTML (<b>жирный</b>, <i>курсив</i>)
2 — Markdown (**жирный**, _курсив_, `моноширинный`)
Все сообщения попадают в очередь и отправляются не чаще одного в 2 секунды — лимит MAX не нарушается.
Шаг 6. Автоматические уведомления
Скрипт уже содержит обработчик OnTrade — при каждой сделке в QUIK на телефон автоматически уходит уведомление:
text
?? Прод **RIZ5** 5 @ 105250.00
?? Покуп **GAZP** 100 @ 198.50
Чтобы добавить свои триггеры, допишите в скрипт или в отдельный файл:
lua
-- Пример: уведомление при изменении цены
function OnQuote(class_code, sec_code)
if sec_code == "RIZ5" then
local ql = getQuoteLevel2(class_code, sec_code)
if ql and #ql.bid > 0 then
local bid_price = tonumber(ql.bid[#ql.bid].price)
if bid_price and bid_price > 105000 then
SendToMAX("RIZ5 выше 105000! Текущая bid: " .. bid_price, nil, 2)
end
end
end
end
Шаг 7. Остановка
В окне Lua-скрипты нажмите Остановить.
Скрипт отправит в MAX сообщение: ?? MAX Bridge остановлен. (или с количеством неотправленных сообщений в очередях).
COM-объект освобождается, связь закрывается.
Типичные проблемы
Скрипт запущен, но /start не приходит:
Проверьте, что токен вставлен правильно, без пробелов.
Проверьте интернет-соединение на ПК.
В окне сообщений QUIK посмотрите ошибки — если WinHttpRequest не создан, возможно, COM заблокирован антивитором.
Сообщения приходят с задержкой:
Это нормально — rate limit 2 секунды. Если в очереди 5 сообщений, последнее уйдёт через 10 секунд.
Если задержка больше минуты — проверьте, не вырос ли ERR_COUNT (ошибки сети). При ошибках polling замедляется.
luacom не найден:
В некоторых сборках QUIK библиотека luacom не установлена по умолчанию.
Скачайте luacom.dll и положите в папку QUIK рядом с остальными .dll.
Кириллица в сообщениях ломается:
Скрипт отправляет в кодировке UTF-8.
Если QUIK отдаёт строки в Windows-1251 (что типично), кириллица может прийти кривой.
В этом случае нужно конвертировать текст перед отправкой — скажите, добавлю функцию перекодировки.
Если торгуете акциями, а не фьючерсами — замените FUTURES_CLIENT_HOLDING на нужную таблицу в build_reply для команды /positions.
--]]