Блог им. jatotrade_com
stopped = false -- Остановка файла socket = require("socket") -- Указатель для работы с sockets json = require( "json" ) -- Указатель для работы с json IPAddr = "127.0.0.1" --IP Адрес IPPort = 3585 --IP Port client = nil -- Функция вызывается перед вызовом main function OnInit(path) -- create a TCP socket and bind it to the local host, at port IPPort server = assert(socket.bind("*", IPPort)) message(string.format("Server started. IP: %s; Port: %d\n", IPAddr, IPPort), 1); end; -- Функция вызывается перед остановкой скрипта function OnStop(signal) if client then client:close() end stopped = true; -- Остановили исполнение кода end; -- Функция вызывается перед закрытием квика function OnClose() if client then client:close() end stopped = true; -- закрыли квик, надо остановить исполнение кода end; --Этой функцией заменен парсер. Саму ее нужно применять с осторожностью, т.к. это может нарушить безопасность системы. --Она позволяет выполнять любой луа-код, поступивший со стороны клиента function evalString (str) return assert(loadstring( "return " .. str))() end -- Основная функция выполнения скрипта function main() -- wait for a connection from any client client = server:accept() sleep(1) while not stopped do local line, err = client:receive() -- if there was no error, send it back to the client local result = evalString(line) if result == nil then result = "{}" end if type(result) == "table" then result = json.encode(result) end if type(result) == "boolean" then result = (result and 1 or 0) end -- if not err then message(string.format("Message:%s Result: %s\n", line, result), 1) end if not err then client:send(result .. "\n") end end end;
# 1.В Квике запускаем скрипт LuaQuikServer.lua # 2.В Питоне эту ячейку CTRL+Enter import socket import json import sys from datetime import datetime host = '127.0.0.1' #локальный компьютер port = 3585 #порт для подключения к серверу # Буфер для получения ответа от сервера QLUA. Будьте внимательны, если ответ длиннее buff, увеличивайте размер buff заранее buff=2048 account_forts = '888097i' # Аккаунт на ФОРТСе account_tplus = 'L01+00000F00' # Аккаунт на Мамбе client_tplus = '888VB/888VB' # Код клиента на Мамбе transid = 1 # Инкрементируемый ID транзакции #Функция - пример того, как из Python (или любого другого клиента, в т.ч. удаленного) можно вычислить любое корректное #выражение или функцию на стороне сервера QLua и вернуть результат в Python, например res=QLua("1+2"). #Аргументом является строка, содержащая корректное выражение QLua. Т.к. в Квике присутствует кириллица формат будет 'cp1251' #Строка посылается через сокет на QLua-сервер, вычисляется на нем, а результат возвращается в Python. #Всегда используйте это с осторожностью, особенно если КВИК находится не на локальном хосте а в сети. Т.к. подключаемый #к QLua-серверу клиент получает полный контроль над Квиком. Чтобы избежать проблем с безопасностью, замените в QLua-сервере #"вычислитель" на парсер или используйте SSL-соединение. def QLua(any_eval_string): s.send(f'{any_eval_string}\n'.encode('cp1251')) return s.recv(buff).decode('cp1251').replace("\n","") #Функция предназначена для определения состояния подключения клиентского места к серверу. #Возвращает 1, если клиентское место подключено и 0, если не подключено. def isConnected(): s.send('isConnected()\n'.encode('utf-8')) return int(s.recv(buff).decode('utf-8')) #Функция предназначена для получения информации по бумаге. Возвращает словарь, соответствующей таблице QLua с параметрами. def getSecurityInfo(class_code, sec_code): s.send(f'getSecurityInfo("{class_code}", "{sec_code}")\n'.encode('utf-8')) return json.loads(s.recv(buff).decode('cp1251').replace("\n","")) #Функция для выставления лимитных и стоп-заявок. Аргументы ticker - словарь, содержащий описание тикера после запроса # getSecurityInfo(class_code, sec_code). Например SiU0=getSecurityInfo("SPBFUT", "SiU0"). price - цена, quant -количество # контрактов(лотов) в заявке, oper 'B'(по умолчанию) или 'S' - покупка или продажа, stop_price - цена активации стоп-заявки, # если указана идентифицирует заявку как стоп-заявку. acc - торговый счет клиента (по умолчанию на ФОРТСе) # Примеры выставления заявок: # Лимитная заявка на покупку 1 контракта SiU0 по цене 70059. NewOrder(SiU0, 70059, 1, oper = 'B') # Лимитная заявка на продажу 1 контракта SiU0 по цене 70888. NewOrder(SiU0, 70888, 1, oper = 'S') # Для выставления стоп-заявки, задайте явно аргумент stop_price. Это цена активации стоп-заявки. # Стоп-заявка на покупку 10 контрактов SiU0. При достижении ценой 71008 Купить 10 контрактов по цене 71028. # NewOrder(SiU0, 71028, 10, oper = 'B', 71008) def NewOrder(ticker, price, quant, oper = 'B', stop_price = False, acc = account_forts): global transid transid += 1 #Увеличиваем счетчит транзакций act = "NEW_STOP_ORDER" if stop_price else "NEW_ORDER" #Если указана цена активации заявки stop_price - это стоп-заявка stop_price_str = f'["STOPPRICE"] = "{stop_price}",' if stop_price else "" #если заявка лимитная, то это строка пустая s.send(('sendTransaction ({'+ f'["TRANS_ID"] = "{transid}",["ACTION"] = "{act}",["CLASSCODE"] = "{ticker["class_code"]}",["SECCODE"] = "{ticker["sec_code"]}",["OPERATION"] = "{oper}",["QUANTITY"] = "{quant}",["PRICE"] = "{price}",{stop_price_str}["ACCOUNT"] = "{acc}"' + '})\n').encode('utf-8')) return s.recv(buff).decode('cp1251').replace("\n","") #Функция для снятия лимитных заявок по идентификатору заявки order_id - число или строка. # transid - необязательный (в Питоне) номер транзакции. acc - торговый счет клиента (по умолчанию на ФОРТСе) def LimitOrderKill(ticker, order_id, transid = transid, acc = account_forts): s.send(('sendTransaction ({'+ f'["TRANS_ID"] = "{transid}", ["ORDER_KEY"] = "{order_id}",["ACTION"] = "KILL_ORDER",["CLASSCODE"] = "{ticker["class_code"]}",["SECCODE"] = "{ticker["sec_code"]}",["ACCOUNT"] = "{acc}"' + '})\n').encode('utf-8')) return s.recv(buff).decode('cp1251').replace("\n","") #Функция для снятия стоп-заявок по идентификатору заявки order_id - число или строка. def StopOrderKill(ticker, order_id, transid = transid, acc = account_forts): s.send(('sendTransaction ({'+ f'["TRANS_ID"] = "{transid}", ["STOP_ORDER_KEY"] = "{order_id}",["ACTION"] = "KILL_STOP_ORDER",["CLASSCODE"] = "{ticker["class_code"]}",["SECCODE"] = "{ticker["sec_code"]}",["ACCOUNT"] = "{acc}"' + '})\n').encode('utf-8')) return s.recv(buff).decode('cp1251').replace("\n","") #################################################### Python Client Code ############################################################### #Сокет для отправки комманд в КВИК и получения результатов исполнения (через сервер в луа-скрипте) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1',3585))
#По традиции поздороваемся с Квиком: res = QLua('message("Привет, Квик!", 1)') res #Проверим знание Квиком арифметики 1-го класса: res = QLua('3+2') res #Проверим, подключен ли Квик к торгам, уже с помощью функции в Питоне(см.код выше) isConnected()
#В переменную-словарь SiU0 запросим из Квика все параметры контракта SiU0."SPBFUT" - код площадки, "SiU0" - код инструмента SiU0 = getSecurityInfo("SPBFUT", "SiU0") SiU0
#Выставим лимитную заявку на покупку 1 контракта SiU0 по цене 70059 res=NewOrder(SiU0, 70059, 1, 'B') res #Выставим стоп на продажу 1 контракт с ценой активации стопа 69888 и ценой исполнения 69850 res=NewOrder(SiU0, 69850, 1, 'S', 69888) res
#Снимем лимитную и стоп-заявки по номеру заявки LimitOrderKill(SiU0, 26040408900600) StopOrderKill(SiU0, 149077121)
#ОСТОРОЖНО!!! КОД ВЫСТАВЛЯЕТ В ЦИКЛЕ 10 КОНТРАКТОВ SiU0 НА ПОКУПКУ ПО ЦЕНЕ 70200 И НИЖЕ С ШАГОМ В 2 ПУНКТА for price in range(70200, 70180 , -2): NewOrder(SiU0, price, 1, 'B')
#После окончания успешной торговли не забывайте закрывать соединение, а в Квике остановите луа-скрипт. s.close() s
#По традиции поздороваемся с Квиком:
res = QLua('message(«Привет, Квик!», 1)')
res
#Проверим знание Квиком арифметики 1-го класса:
res = QLua('3+2')
res
#Проверим, подключен ли Квик к торгам, уже с помощью функции в Питоне(см.код выше)
isConnected()
но вот я не смог придумать как отладить скрипт на lua в квике. если только через логи в фаил. где хранить данные робота? интерфейс с базой данных?
Потокобезопасность обеспечивают функции table.sinsert(), sremove().
Но мои роботы хранят торговые позиции для перехода на следующий день в Lua-скиптах, содержащих соответствующие таблицы.
А отлаживают по складам (через дебагер) и читают по складам только первоклашки. Хотя в Lua есть модуль debug.
Но роботов я отлаживаю на демо-счетах.
QLua не «просто инструмент». Это идеальный инструмент.
server = assert(socket.bind(«127.0.0.1», IPPort))
и запускать клиент и сервер на одной и той же машине.
Может, хреновый из меня айтишник (но я и не полностью айтишник), но так приятно когда тебя за ручку проводят через сложные процессы где много где можно оступиться и завязнуть.
Всегда когда что-то подобное делаешь, всегда вылезет куча нюансов:
— чет не работает.
— а, ну тут библиотека с этой не совместима.
— чет не работает, а ну тут же файл надо переименовать, на стэковерфлоу в 2007 году пост на эту тему был, не читал что ли?
— чет не работает.
— а, ну ты в реестре запись, наверно, не прописал и конечно же не отключил системный процесс А.
А тут такое:
— вот тебе папочка, скопируй её сюда, да смотри не перепутай — содержимое, а не всю папку.
Расписано какие конкретно как и зачем ячейки в ноутбуке запускать.
Может, я сегодня какой-то особенно сентиментальный, но это праздник какой-то)), так иногда хочется чтоб тебя вот так юзер-френдли сопровождали при решении сложных задач, почему весь мир IT не такой?))).
Респект ещё раз!
Подружил с php (пока на тест) — то что давно хотел сделать )
Правда при клозе скрипт ваш по такой ошибке выпадает сам...
WebQuik <--> Websocket <--> Python
Messages ID 2xxxx — from server to client
https://junior.webquik.ru/classes/controller/CtrlWSocket.js
https://github.com/loktevsp/TraderQUIK/blob/master/dataServer.txt
github.com/loktevsp/TraderQUIK/blob/master/dataServer_заявки.txt
https://github.com/DmitryPukhov/pytrade/blob/master/pytrade/connector/quik/MsgId.py
https://github.com/m13oas/quik-connector
https://github.com/eSKond/WrapWebQuik
Господа, проконсультируйте, пожалуйста, по некоторым вопросам. Пробую использовать питон для написания простенькой автоматической торговой системы. Питон выбрал, так как уже имел с ним небольшой опыт, да и язык универсальный, может где ещё пригодится. Но, как я погляжу, не очень то распространено его использование в связке с торговыми терминалами, поэтому приходится собирать информацию по крупицам, и публикации Евгения Шибаева, похоже, самый лучший источник на смартлабе, если не сказать больше-в рунете! :) Для начала завёл демо-счёт у финама и установил предложенный Квик 7.25. Луа-серверы из постов «Управление заявками в Квике из Питона» и «Стакан к празднику» как-то можно между собой совместить? Можно ли запустить их одновременно разными потоками? (уже попробовал-можно). Что для этого нужно, использовать разные порты? Я смотрю, что в одном скрипте используется порт 3585, а в другом 3587, это имеет какое-либо значение? Или совместить эти скрипты в одном файле? Вообще, сейчас попробовал запускать скрипты, но никаких данных не получил, возможно это связано с тем, что это было в воскресенье, торги не ведутся.
Что за аккаунт на фортс и мамбе упоминается в посте? Это те, которые используются для подкачки исторических данных? В квике в окне заявки я вижу «торговый счёт» и «код клиента». Это оно?
Как на питоне скачать в рантайме неглубокие исторические данные, что бы строить, скажем, скользящую среднюю? Для оптимизации параметров алгоритма на большой истории я просто скачиваю csv с сайта финама, а как быть с рантаймом?
Или вообще не заморачиваться, и работать с каким-нибудь TSLab? Но хотелось бы с питоном поковыряться, интересно же..