Блог им. jatotrade_com

КВИК-->Lua-->Python. Стакан к празднику.

Всем привет, с наступающим праздником! Который, надеюсь у большинства пройдет, как обычно, в ЖО ЗОЖе (блин, слово-то придумали).
В продолжение топика "КВИК-->Lua-->Python. Трансляция данных из КВИКа в Питон в реальном времени".
В Python-сервер добавлен парсер и визуализатор стакана. Стакан в стиле QSCALP-лайт вариант. Все как обычно в 20 строк кода.

У Тимофея гифки со сторонних сайтов не кажут. Приходится ссылку давать… Или отказываться от главной. Выбрал второе.
КВИК-->Lua-->Python. Стакан к празднику.Чтобы насладиться созерцанием стакана нам нужны следующие ингредиенты:
1. Квик версии 8.5.2 и выше.
2. Lua-скрипт QuikLuaPython.lua (собственно сокет-клиент)
3. Питон (Jupyter Notebook Anaconda 3)
4. Python_QUIK_Server.ipynb (собственно сокет-сервер)
Считаем, что Квик и Питон у вас уже установлены. Чтобы запустить трансляцию, скачайте папку PythonServer в ней вы найдете все необходимое. Файл Python_QUIK_Server.ipynb поместите в папку Питона (чтобы его видел Jupyter Notebook). Затем, содержимое папки QUIK8.5.2(а не саму папку!) скопируйте в папку Квика. В Квике, в меню «Сервисы»-«Луа-скрипты» добавьте луа-скрипт QuikLuaPython.lua.
Запустите сервер в питоне (CTRL+Enter в первой ячейке файла Python_QUIK_Server.ipynb). Затем запустите луа-скрипт в Квике (начнется выгрузка данных с истории обезличенных сделок). И, наконец, запустите визуализацию графика и стакана в Питоне - CTRL+Enter во второй ячейке файла Python_QUIK_Server. Если вы все сделали правильно, то появится (возможно со второго раза) картинка примерно как в начале топика.
Сервер в Питоне:
#1.Запускаем сервер (эту ячейку) CTRL+ENTER
#2.В КВИКе запускаем луа-скрипт QuikLuaPython.lua
import socket
import threading
import pandas as pd

ticker = 'BRN0' #В Квике должен быть открыт стакан BRN0, а в таблице обезличенных сделок транслироваться тики BRN0
ticks=[] #Для примера, список обезличенных сделок BRN0
stakan = '' # строка формата '"имя тикера" {bid_price:bid_size,bid_price1:bid_size1...ask_price:-ask_size,ask_price1:-ask_size1}'

def parser (res):
    parse = res.split(" ", 2) #первый элемент - идентификатор события, второй имя тикера
    if parse[0] == '2': # парсинг стакана (событие '2')
        if parse[1] == ticker:
            global stakan
            stakan = parse[2]
    if parse[0] == '1': # парсинг обезличенной сделки (событие '1')
        tail = res.split(" ")
        if tail[1] == ticker: #записываем цену текущего тика BRN0 в список ticks
            ticks.append(float(tail[4]))

#Собственно сервер            
def service():
    sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    sock.bind(('127.0.0.1',3587)) #Локальный хост-этот компьютер, порт - 3587
    while True:
        res = sock.recv(2048).decode('utf-8')
        if res == '<qstp>\n':  #строка приходит от клиента при остановке луа-скрипта в КВИКе
            break
        else:
            parser(res) #Здесь вызываете свой парсер. Для примера функция: parser (parse)
    sock.close()

#Запускаем сервер в своем потоке
t = threading.Thread(name='service', target = service)
t.start()

Аниматор в Питоне:
#3.Запускаем отображение стакана и графика тиков (эту ячейку) CTRL+ENTER
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
%matplotlib notebook

hi_lo = [0.0, 0.45] #здесь достаточно задать верхнюю и нижнюю границу цены стакана, чтобы сохранить заданный масштаб
max_vol = 3000 #максимальное значение шкалы объема на стакане
max_ticks = 1000 #Количество отображаемых тиков на графике
fsize = 10 #размер шрифта в стакане

fig = plt.figure(figsize=(12, 9), dpi= 80) #Определяем размер изображения
ax_stakan = fig.add_subplot(1, 4, 3) #Расположение стакана
ax_tick = fig.add_subplot(1, 2, 1) #Расположение графика сделок
ax_stakan.tick_params(left = False, labelleft = False, labelright = True, labelsize=8) #Расположение осей

#Функция для автосмещения шкал при приближении текущей цены к верхней или нижней границам стакана (20 процентов)
#point - количество знаков после запятой, например для BR - 2, а для RI, SI, SR, GZ - 0, или можно не задавать
def masht(price, point = 0):
    percent = 0.2
    hi, lo = hi_lo[1], hi_lo[0]
    rn = hi - lo
    if price < lo + rn*percent or price > hi - rn*percent:
        hi_lo[0]=round(price - 0.5*rn, point)
        hi_lo[1]=round(price + 0.5*rn, point)

#Функия обновления кадра графика
def update(i):
    if ticks:
        price = ticks[-1] #Цена последней сделки
        ob_dict = eval(stakan) #читаем из строки стакан-словарь в ob_dict
        masht(price, 2) #проверяем необходимость смещения High,Low диаграммы при достижении ценой соответствующей границы
        ax_tick.clear()
        ax_stakan.clear()
        ax_tick.grid(which='major', color = 'gray', linestyle = ':')
        ax_stakan.grid(which='major', color = 'gray', linestyle = ':')

        ax_stakan.set_ylim(hi_lo)
        ax_stakan.set_xlim([0,max_vol])
        ax_tick.set_ylim(hi_lo)
        ax_tick.set_xlim([0,max_ticks])
        
        bid_price, bid_size, ask_price, ask_size = [],[],[],[]
        #Распределяем стакан-словарь на 4 списка: bid_price, bid_size, ask_price, ask_size
        for (pr, sz) in ob_dict.items():
            if pr >= hi_lo[0] and pr <= hi_lo[1]: #отображаем только объемы в видимой области стакана
                if sz > 0:
                    bid_price.append(pr)
                    bid_size.append(sz)
                else:
                    ask_price.append(pr)
                    ask_size.append(-sz)
        x, y = np.array(bid_price), np.array(bid_size)
        x1, y1 = np.array(ask_price), np.array(ask_size)
        ax_stakan.autoscale(False, tight=False) #Отключаем автошкалу Питона, чтобы график не "прыгал" постоянно
        ax_stakan.barh(x, y, height = 0.009, color = 'seagreen', alpha=0.4) #tick_label = bid_size
        ax_stakan.barh(x1, y1, height = 0.009, color = 'lightcoral', alpha=0.4)
        for (pr, sz) in zip(bid_price[1:], bid_size[1:]): #Печать объемов
            ax_stakan.text(0, pr, sz, fontsize=fsize, va='center', color='green') #darkgreen
        for (pr, sz) in zip(ask_price[:-1], ask_size[:-1]):
            ax_stakan.text(0, pr, sz, fontsize=fsize, va='center', color='red') #darkred
        #Маркер текущей цены        
        ax_tick.text(max_ticks*1.01, price, price, fontsize=10, va='center',ha='left')
        ax_tick.plot(ticks[-max_ticks:]) #Отображаем последние 1000(max_ticks) тиков

# Включаем анимацию на 1000 кадров, раз в 400 мсек
ani = matplotlib.animation.FuncAnimation(fig, update, frames=1000, repeat=False, interval=400, blit=True)
plt.show()

Lua-скрипт для Квика:
stopped = false            -- Остановка файла
socket = require("socket") -- Указатель для работы с sockets
json = require( "json" ) -- Указатель для работы с json
IPAddr = "127.0.0.1"       --IP Адрес
IPPort = 3587		   --IP Port	 
sender = nil		   --Укзатель на коннектор
send = nil		   --Указатель на процедуру отправки сообщения
connect = nil		   --Указатель на процедуру подключения к серверу
all_trd_indx = 0	   --Текущий индекс для переданных записей таблицы всех сделок
all_ord_indx = 0
all_stop_ord_indx = 0
all_my_trades_indx = 0
inited = false		   --Для проверки вызова OnAllTrade перед main

-- Функция вызывается перед вызовом main
function OnInit(path)
--  sender = assert(socket.connect(IPAddr, IPPort));
  sender = socket.udp()
  sender:setpeername(IPAddr, IPPort)
  --Выводим сообщение в квике, что есть подключение
  message(string.format("Connection success. IP: %s; Port: %d\n", IPAddr, IPPort), 1);
  sender:send("<qsrt>\n");
end;

-- Функция вызывается перед остановкой скрипта
function OnStop(signal)
  sender:send("<qstp>\n");
  stopped = true; -- Остановили исполнение кода 
end;

-- Функция вызывается перед закрытием квика
function OnClose()
  sender:send("<qcls>\n");
  stopped = true; -- закрыли квик, надо остановить исполнение кода
end;

-- Функция вызвается при изменении стакана

function OnQuote(class, seccode)
  if stopped then return; end
  local sec = getSecurityInfo(class, seccode);
  local price = 0;
  local level2 = getQuoteLevel2(class, seccode); --Получаем стакан
  --сначала загружаем бид, потом аск
  local bidstr = " {"
  if type(level2["bid"]) == "table" then
  	for index, bid in ipairs(level2["bid"]) do
  	  bidstr = bidstr..bid["price"]..":"..bid["quantity"]..","
  	end
  end;
  local askstr = ""
  if type(level2["offer"]) == "table" then 
    for index, ask in ipairs(level2["offer"]) do
	  askstr = askstr..ask["price"]..":-"..ask["quantity"]..","
    end
  end;
  local str = "2 "..seccode..bidstr..askstr.."}\n";
  sender:send(str)
end;

--User trades functions
function formatmytrade (status, trade)
  all_my_trades_indx = all_my_trades_indx + 1
  return status.." "..trade["sec_code"].." "..trade["trade_num"].." "..trade["order_num"].." "..trade["flags"].." \""..trade["account"].."\" "..trade["price"].." "..trade["qty"].." "..trade["value"].." \""..trade["brokerref"].."\" "..trade["datetime"]["day"].."."..trade["datetime"]["month"].."."..trade["datetime"]["year"].." "..trade["datetime"]["hour"]..":"..trade["datetime"]["min"]..":"..trade["datetime"]["sec"].." "..trade["trans_id"].."\n"
end;

function OnTrade(trade)
  if (not inited) or (stopped) then return; end;
  sender:send (formatmytrade (8, trade));
end;

function sendallmytrades()
  local count = getNumberOf("trades");
  local index = 0
  for index=0,count - 1 do
  	if stopped then return false; end;
  	sender:send (formatmytrade (8, getItem("trades", index)));
  end;
  return true;
end;

--User stop orders functions
function formatstoporder (status, order)
  all_stop_ord_indx = all_stop_ord_indx + 1
  return status.." "..order["sec_code"].." "..order["trans_id"].." "..order["order_num"].." "..order["flags"].." \""..order["account"].."\" "..order["price"].." "..order["condition_price"].." "..order["qty"].." "..order["balance"].." \""..order["brokerref"].."\" "..order["ordertime"].."\n"
end;

function OnStopOrder (order)
  if (not inited) or (stopped) then return; end;
  sender:send (formatstoporder (7, order));
end;

function sendallstoporders()
  local count = getNumberOf ("stop_orders");
  local index = 0
  for index=0,count - 1 do
  	if stopped then return false; end;
  	sender:send (formatstoporder (7, getItem ("stop_orders", index)));
  end;
  return true;
end;

function formatorder (status, order)
  all_ord_indx = all_ord_indx + 1
  return status.." "..order["sec_code"].." "..order["trans_id"].." "..order["order_num"].." "..order["flags"].." \""..order["account"].."\" "..order["price"].." "..order["qty"].." "..order["balance"].." "..order["value"].." \""..order["brokerref"].."\" "..order["datetime"]["day"].."."..order["datetime"]["month"].."."..order["datetime"]["year"].." "..order["datetime"]["hour"]..":"..order["datetime"]["min"]..":"..order["datetime"]["sec"].."\n"
--  return status.." "..json.encode(order).."\n"
end;

function OnOrder(order)
  if (not inited) or (stopped) then return; end;
  sender:send (formatorder (6, order));
end;

function sendallorders()
  local count = getNumberOf("orders");
  local index = 0
  for index=0,count - 1 do
  	if stopped then return false; end;
  	sender:send (formatorder (6, getItem("orders", index)));
  end;
  return true;
end;

function OnAllTrade(trade)
  if (not inited) or (stopped) then return; end;
  --Отправляем сделку
  sender:send(formattrade1(1, trade));
end;

function formattrade1(status, trade)
  all_trd_indx = all_trd_indx + 1 -- Увеличиваем счетчик кол-ва 
  --Формируем запись для передачи
  return status.." "..trade["sec_code"].." "..all_trd_indx.." "..trade["trade_num"].." "..trade["price"].." "..trade["flags"].." "..trade["qty"].." "..trade["datetime"]["day"].."."..trade["datetime"]["month"].."."..trade["datetime"]["year"].." "..trade["datetime"]["hour"]..":"..trade["datetime"]["min"]..":"..trade["datetime"]["sec"].."."..trade["datetime"]["ms"].."\n"
--[[  trade_num NUMBER  Идентификатор сделки
flags NUMBER  Набор битовых флагов
price NUMBER  Цена
qty NUMBER  Количество
value NUMBER  Объем сделки
accruedint  NUMBER  Накопленный купонный доход
yield NUMBER  Доходность
settlecode  STRING  Код расчетов
reporate  NUMBER  Ставка РЕПО
repovalue NUMBER  Сумма РЕПО
repo2value  NUMBER  Объем сделки выкупа РЕПО
repoterm  NUMBER  Срок РЕПО в днях
sec_code  STRING  Код инструмента
class_code  STRING  Код класса
datetime  TABLE Дата и время
]]
end;

--Загрузка обезличенных сделок
function sendalltrades()
  local count = getNumberOf("all_trades");
  sender:send("4 "..count.."\n");
  local index = 0
  for index=0,count - 1 do
  	if stopped then return false; end;
  	sender:send(formattrade1(3, getItem("all_trades", index)));
  end; 
  sender:send("5\n");
  return true;
end;

-- Основная функция выполнения скрипта
function main()
  inited = true
  if not stopped then
        local start_time = os.clock()
        if sendalltrades() then
           message("Send all trades history success: " .. tonumber(os.clock() - start_time), 1);
           sendallorders()
           sendallstoporders()
           sendallmytrades()
 	   while not stopped do
  	          sleep(1);
  	    end; --while
  	  end; --if  
  end; --if
  sender:send('<qbye>\n')
  sender:close() --закрываем соединение
  sender=nil
end;

ВНИМАНИЕ! ИЗМЕНЕНИЯ К ПРЕДЫДУЩЕЙ ВЕРСИИ.
В луа-скрипте:
1. Стакан из Квика формируется в виде строки:
'2 «BRN0» {40.47:23,40.48:2,40.49:36,40.50:628,....40.96:536,40.98:-465,40.99:-758,41.00:-823,....41.45:-21,41.46:-25,41.47:-12}'
где — 2 — изменение «стакана», «BRN0» — имя тикера, далее словарь из пар Price:Size упорядочен по возрастанию цены. Если  Size положительный — это бид, отрицательный — аск.
2. Добавлена библиотека json (она пригодится нам в процессе управления заявками, подробно опишу в следующем топике)
Поэтому, если вы пользовались старой версией, обновите луа-скрипт и добавьте модуль json.lua

Кстати, если кто не знал, к серверу можно подключать любое(в разумных пределах) количество Квиков (клиентов). Для этого в луа-скрипте добавьте перед сообщением UID конкретного Квика для идентификации его на стороне Питона. Но это уже другая история.

Все коды можно, также, найти в конце страницы загрузки Jatotrader.
Кое что интересное для извлечения денег с рынка можно посмотреть на моем канале в ютьюбе.
Всем удачи! И будьте здоровы!
  • обсудить на форуме:
  • QUIK
★53 | ₽ 1
40 комментариев
Купил фьюч на звезды к этому посту с 6-м плечом.

Продам на 50 звездах ★★★★★....

Потому, что пост про LUA))
avatar
$100, добавляю лонга на 7
Евгений Шибаев, едем на север вместе))
avatar
Может когда и пригодится. Всегда рад видеть единомышленников.
Но у меня другая концепция:
— Питон для объемных вычислений,
— для всяких стаканов/сделок есть С++.
— для связи — Луа.
avatar
3Qu, это я так, чисто проверить получаю ли я в Питоне стаканы с тиками))) для наглядности, чтобы не вызывать каждый раз переменные ticks и stakan.
На самом деле, коллега, питон в этом варианте очень прожорлив, естессно его никто не собирается использовать для отображения быстрой графики.
Эй «питонисты» — народ плечистый, кто знает, почему в юпитер ноутбуке анимация (при интерактивном режиме) запускается только со второго раза, при первом дает:

Traceback (most recent call last):
File «C:\Users\User\Anaconda3\lib\site-packages\matplotlib\cbook\__init__.py», line 216, in process
func(*args, **kwargs)
File «C:\Users\User\Anaconda3\lib\site-packages\matplotlib\animation.py», line 1465, in _stop
self.event_source.remove_callback(self._loop_delay)
AttributeError: 'NoneType' object has no attribute 'remove_callback'

Евгений Шибаев, не знаю, я в Spyder работаю. Там все ОК. Окна делаются, в них графики, кнопки и пр радости.
avatar
3Qu, да, в спайдере проблем не наблюдается, я про ноутбук интересуюсь.
Евгений Шибаев, а просто наплевать и забить на этот Юпитер.?
avatar
Евгений Шибаев, я бы попробовал перенести
%matplotlib notebook в самое начало.
avatar
/../, Спасибо большое, так и есть.
Comrade, saludos cordiales! El pueblo unido hamas sera vensido!
Евгений Шибаев, тема закрыта, спасибо /\../

я бы попробовал перенести
%matplotlib notebook в самое начало.

У Тимофея гифки со сторонних сайтов не кажут. Приходится ссылку давать...
Пока не было комментариев, на картинке была прекрасная анимация.
Сейчас вот это уточнение и статическое изображение.
Странно, может совпадение ?
А так, разумеется, спасибо!
avatar
Rymys, да была, сам порадовался. Только от Тимофея малява пришла, что с главной страницы снимут за использование внешних источников встроенных в хтмл код. Ну приеду домой вечером, верну гифку, я не гордый мне на главную не обязательно. А кому нужно — найдут.
Полезно. Спасибо!
avatar
Да, хорошая работа.
Вам большой респект и дальнейших творческих успехов.
avatar
_sg_, за оценку, спасибо! Работа небольшая — строчек кода мало. Больше время уходит на обдумку.

А где взять библиотеки для Lua:

socket = require("socket") -- Указатель для работы с sockets
json = require( "json" ) -- Указатель для работы с json
Фима Гирин, https://yadi.sk/d/2gQ2gZOEPztIaw по этой ссылке в папке QUIK8.5.2 лежат все библиотеки луа
Евгений Шибаев, Спасибо
Интересно, а как эта визуалицация стакана помогает жить (зарабатывать) трейдеру?
Сделали бы лучше что-то типа скринера для акций для Квика. Например, чтоб в таблицу можно было выбрать интересующие инструменты и по ним отображалось по каждому инструменту изменение текущей цены по отношению к цене с задаваемым условием: за неделю, месяц, или особенно здорово например от последнего хай/лой за последние хх дней/ определенной даты.
avatar
Weddy, Интересно, а как эта визуалицация стакана помогает жить (зарабатывать) трейдеру?
Речь в топике не про заработок, и даже не про визуализацию (она в таком виде на питоне ресурсоемка). Тема — получение данных в Питоне из Квика в реальном масштабе времени, т.е. мгновенно. А по скринеру, ну что ж, на досуге сделаю специально для вас (если это поможет зарабатывать). Думаю уложусь в 7-8 строчек кода.
А по скринеру, ну что ж, на досуге сделаю специально для вас (если это поможет зарабатывать).
Вот будет здорово! А сортировать полученные значения по возрастанию/убыванию можно будет?
Не сочтите за наглость, а можно ли еще к таблице добавить столбец, в котором пользователь мог бы просто заносить свои примечания к инструменту в произвольном формате?
Все это правильно было бы просить у разработчика. Но зайдя на форум Арки и полазив я увидел, что там годами висят предложения/просьбы к продукту от пользователей без всякого движения. Уж сколько лет народ просит добавить очевидные вещи типа автоматического выставления стопа/тэйк-профита по совершению сделки — все в пустоту.
avatar
Weddy, забудьте про Арку — там оч. сильно занятые ребята. Если обещал, сделаю, и столбцы любые можно будет добавлять, и сортировку если надо — но это уже не 7-8 строчек будет, а 9-10 — придется попотеть))). От вас только чуток знания питон-кода и все.
Евгений Шибаев, 
От вас только чуток знания питон-кода и все.
Я не только с Питоном, я и с другими языками не знаком. Помню только в районе 1986 г на лабораторных через перфокарты на Фортран-IV вводил какие-то простейшие «программы» типа выполнения простейших арифметических  вычислений )))
А средствами только скрипта Lua задачи построения такой таблицы не решаются?
avatar
Weddy, ))) а я сначала не понял вас, думал что вы питон-код имели ввиду. С помощью луа-скрипта задача, также решается, я правда в луа не слишком силен, но думаю справиться. Т.к. в этом случае одно лишнее звено выпадает (питон). Единственный момент, в этом случае вы сможете получать эти данные, только при подключенном Квике. Тогда по времени небольшой таймаут возьму, изучу как таблицы строить в луа.
Евгений Шибаев, 
Т.к. в этом случае одно лишнее звено выпадает (питон).
С точки пользователя это значимо. Т.к. для незнакомого с этим не надо будет запускать еще и неведомый ему сервер в питоне.
Единственный момент, в этом случае вы сможете получать эти данные, только при подключенном Квике.
Ну собственно вряд ли это проблема. Если таблица для Квика используется при запущенном Квике, то почему бы еще и не подключиться к серверу ))). Лишь бы не другой нюанс: видел несколько раз люди выкладывали луа-скрипты по рисованию горизонтальных объемов на графике в Квике. Почему-то по каждому из них автор указывал, что рисуется только по данным текущей сессии. Словно луа не имеет доступа к историческим данным. Что для моего дилетантского понимания странно слышать. Т.к. Квик же отображает исторические данные. Более того, он даже пишет (как я предполагаю) в папку \archive .dat файлы с данными по просматривавшимся инструментам.
avatar
Weddy, Как понимаю, идея с таблицей не получилась?
avatar
Weddy, нет, я же обещал вам, постараюсь на этой неделе сделать под луа, отпишу обязательно
Евгений Шибаев, ОК, буду ждать
avatar
Weddy, посмотрите, кое-что для вас готово https://smart-lab.ru/blog/630269.php
Евгений Шибаев, спасибо! посмотрел, отписался
avatar
Weddy, вот уже треть задания готово — читаем дневки по инструменту с МОЕХ. В примере SiM0.

import requests
import csv
url='https://iss.moex.com/iss/engines/futures/markets/forts/securities/SIM0/candles/securities.json?&from=2020-03-20&start=0&interval=24&candles.columns=open,close,high,low,volume,end'
res=requests.get(url)
data=res.json()['candles']
data
Возвращает словарь типа:

{'metadata': {'open': {'type': 'double'},
  'close': {'type': 'double'},
  'high': {'type': 'double'},
  'low': {'type': 'double'},
  'volume': {'type': 'double'},
  'end': {'type': 'datetime', 'bytes': 19, 'max_size': 0}},
 'columns': ['open', 'close', 'high', 'low', 'volume', 'end'],
 'data': [[75251, 68170, 75251, 68170, 2, '2018-06-20 23:59:59'],
  [67362, 67299, 67362, 67295, 6, '2018-07-31 23:59:59'],
  [67892, 67892, 67892, 67892, 1, '2018-08-03 23:59:59'],
  [71500, 71500, 71500, 71500, 2, '2018-08-10 23:59:59'],
  [67810, 66447, 67850, 66420, 132406, '2020-03-03 23:59:59'],
  [66400, 66967, 67195, 66284, 74240, '2020-03-04 23:59:59'],<br />  [.......................................................................................]
  [67000, 68000, 68028, 66890, 61080, '2020-03-05 23:59:59'],
  [67868, 69149, 69484, 67790, 228954, '2020-03-06 23:59:59'],
  [69134, 73213, 76480, 69125, 278930, '2020-03-10 23:59:59'],
  [73159, 73545, 73651, 72266, 184977, '2020-03-11 23:59:59'],
  [73426, 75837, 76869, 73159, 388919, '2020-03-12 23:59:59'],
  [76075, 74753, 76571, 73225, 449726, '2020-03-13 23:59:59'],
  [74767, 75230, 76750, 73350, 564868, '2020-03-16 23:59:59'],
  [75100, 76025, 76399, 74577, 854937, '2020-03-17 23:59:59'],
  [76136, 81127, 82700, 76077, 1803816, '2020-03-18 23:59:59'],
  [81260, 80812, 84250, 80382, 3526861, '2020-03-19 23:59:59'],
  [80830, 80602, 81324, 78500, 4254967, '2020-03-20 23:59:59'],
  [80627, 81336, 82530, 80261, 3396340, '2020-03-23 23:59:59'],
  [81328, 79965, 81667, 79227, 2546482, '2020-03-24 23:59:59'],
  [79985, 79875, 80509, 77969, 3430876, '2020-03-25 23:59:59'],
  [79926, 78008, 80135, 78000, 2979919, '2020-03-26 23:59:59'],
  [78100, 80166, 80374, 78100, 2462034, '2020-03-27 23:59:59'],
  [79860, 80823, 81500, 79860, 2031608, '2020-03-30 19:20:21'],
  [80919, 78808, 80930, 78627, 2656229, '2020-03-31 23:59:59'],
  [78819, 79740, 80343, 78661, 2218093, '2020-04-01 23:59:59'],
  [79765, 78393, 80189, 78080, 3005482, '2020-04-02 23:59:59'],
  [78337, 77877, 78950, 77377, 2609150, '2020-04-03 23:59:59'],
  [77998, 77182, 78060, 76901, 1718334, '2020-04-06 23:59:59'],
  [77150, 76266, 77150, 76012, 1702260, '2020-04-07 23:59:59'],
  [76255, 76271, 76731, 75982, 1465312, '2020-04-08 23:59:59'],
  [76273, 74384, 76395, 73410, 2700594, '2020-04-09 23:59:59'],
  [74402, 74545, 75493, 73926, 1701450, '2020-04-10 23:59:59'],
  [74571, 74369, 74797, 74100, 1336086, '2020-04-13 23:59:59'],
  [74381, 73867, 74457, 73450, 1623728, '2020-04-14 23:59:59'],
  [73886, 75935, 76004, 73696, 2467603, '2020-04-15 23:59:59'],
  [75960, 75040, 76121, 74455, 2580189, '2020-04-16 23:59:59'],
  [75004, 74766, 75367, 74102, 2100606, '2020-04-17 23:59:59'],
  [74751, 75197, 75726, 74519, 1812686, '2020-04-20 23:59:59'],
  [75179, 78175, 78200, 75174, 3747409, '2020-04-21 23:59:59'],
  [78002, 76738, 78357, 76464, 3299366, '2020-04-22 23:59:59'],
  [76680, 75044, 76886, 74861, 2177200, '2020-04-23 23:59:59'],
  [75104, 75200, 75554, 74565, 2263504, '2020-04-24 23:59:59'],
  [75110, 75118, 75428, 74620, 1617248, '2020-04-27 19:20:11'],
  [75359, 74717, 75494, 74342, 2076779, '2020-04-28 23:59:59'],
  [74766, 73857, 74935, 73755, 1532016, '2020-04-29 23:59:59'],
  [73839, 74550, 74655, 73104, 2334129, '2020-04-30 23:59:59'],
  [74550, 75733, 76501, 74550, 2127821, '2020-05-04 23:59:59'],
  [75712, 74213, 75814, 74154, 1807113, '2020-05-05 23:59:59'],
  [74214, 75086, 75514, 73852, 2376231, '2020-05-06 23:59:59'],
  [75067, 74172, 75256, 74046, 2077147, '2020-05-07 23:59:59'],
  [74177, 73608, 74680, 73511, 1708249, '2020-05-08 23:59:59'],
  [73670, 73565, 74160, 73356, 1589536, '2020-05-12 23:59:59'],
  [73568, 74295, 74333, 73472, 2169654, '2020-05-13 23:59:59'],
  [74297, 74320, 74895, 74030, 2789986, '2020-05-14 23:59:59'],
  [74303, 74066, 74352, 73334, 2267306, '2020-05-15 23:59:59'],
  [74064, 72983, 74101, 72874, 1880184, '2020-05-18 23:59:59'],
  [72996, 72855, 73219, 72515, 1984670, '2020-05-19 23:59:59'],
  [72830, 71638, 72919, 71423, 2412846, '2020-05-20 19:19:58'],
  [71509, 71217, 71732, 70879, 2476000, '2020-05-21 23:59:59'],
  [71204, 72020, 72375, 71107, 2779478, '2020-05-22 23:59:59'],
  [72060, 71870, 72109, 71673, 1217504, '2020-05-25 23:59:59'],
  [71866, 71085, 71989, 70832, 1954654, '2020-05-26 23:59:59'],
  [71099, 71488, 71727, 70926, 2308177, '2020-05-27 23:59:59'],
  [71462, 70739, 71462, 70654, 2303205, '2020-05-28 23:59:59'],
  [70721, 70799, 71050, 70350, 2078424, '2020-05-29 23:59:59'],
  [70790, 69621, 70856, 69564, 2385040, '2020-06-01 23:59:59'],
  [69585, 69121, 69626, 68735, 2342788, '2020-06-02 23:59:59'],
  [69130, 68583, 69185, 68351, 2348916, '2020-06-03 23:59:59'],
  [68615, 69478, 69696, 68589, 2577308, '2020-06-04 23:59:59'],
  [69474, 68486, 69502, 68359, 2427644, '2020-06-05 23:59:59'],
  [68518, 68494, 68840, 68134, 2147310, '2020-06-08 23:59:59'],
  [68495, 68617, 69055, 68246, 2227813, '2020-06-09 23:59:59'],
  [68616, 68730, 69026, 68504, 2174627, '2020-06-10 23:59:59'],
  [68729, 69740, 69777, 68270, 3374554, '2020-06-11 23:59:59']]}
Weddy, Тут если ты научишься получать из квика любые данные и отправлять любые управляющие команды, то дальше можно что хочешь накручивать, любые логики какие хочешь. Конечно, немного уметь программировать не помешает, но думаю, если не умеешь — отличный повод изучить Python, тем более не сложный язык).
avatar
Спасибо, красота, заодно посмотрю как подобные вещи пишутся.
avatar
Replikant_mih, сервер для управления заявками у меня готов, нужно в бою еще кое-что допроверить, но это на реале уже в понедельник. Если все будет хорошо — напишу соответствующий топик. Ну там конечно не только заявками управлять можно но и полностью Квиком. Для примера можно Квик использовать просто как калькулятор)))

Евгений Шибаев, Понял, спасибо, супер!)

Блин, мне бы просто посмотреть код которым команда из питона в квик прилетает) и как луашные функции цепляет. Я с луа никогда не работал, но думаю там много и не надо, чтоб функции на команды навесить, просто посмотреть шаблон).

 

Но конечно, если в вашем варианте уже что-то будет реализовано из мясца, не только скелет — вообще отлично).

 

Если что, я с пониманием отнесусь если будет и позже понедельника, а-то тоже, бывает, что-нить скажешь сгоряча, потом приходится делать что обещал))).

avatar
Replikant_mih, да все норм, закон Хеопса никто не отменял — «Ничто и никогда не делается в срок и в пределах сметы».
Там все просто — со стороны луа открывается потоковый сервер, который банально принимает строку от клиента и вычисляет ее. Конечно, небезопасно, если в сети кроме вас есть подключения. Для безопасности просто делается не вычислитель, а парсер на стороне сервера. Ну не будем забегать вперед.
Евгений Шибаев, Питон с питоном сокетами соединял, C# с питоном, а вот с lua… увидел, что кто-то это умеет — теперь вот сижу в засаде, выжидаю))
avatar

При попытке запустить скрипт LUA (QuikLuaPython.lua) на QUIK версии 8.12.0.41 возникает ошибка:
error loading module 'socket.core' from file 'C:\QUIK\socket\core.dll':
Не найден указанный модуль.

C чем может быть связано?
avatar

теги блога Евгений Шибаев

....все тэги



UPDONW