Пожалуйста, помогите удостоверится в правильности отслеживания выполнения транзакций в QUIK. Вопросов у меня много по процессу. Хотелось бы получить ответы на все из них.
Ниже код с вопросами:
Код
-- Utility. Not optimized. Just an example.
function bittest(number, shift)
local v = 1;
for i=1,shift do
v = v * 2;
end
return (number - v) ~= number;
end
running = true;
transaction = {}
transaction_stage = "not send";
transaction_details = {};
function set_stage(new_stage)
transaction_stage = new_stage;
message(transaction_stage, 1);
end
function OnStop()
running = false;
end
function main()
transaction = {
ACCOUNT = "L01-00000***", -- (*** - для приватности)
CLIENT_CODE = "51***//COMMENT", -- лимит 20 символов для этого поля, это верно для транзаций по всем инструментам?
ACTION = "NEW_ORDER",
CLASSCODE = "TQBR", SECCODE = "MTSS",
OPERATION = "B",
TYPE = "M", PRICE = "0", QUANTITY = "1",
TRANS_ID = "1000001", -- какие есть ограничения на значения этого поля? (самые жёсткие, для всех классов инструментов сразу)
};
local result = sendTransaction(transaction);
if (result ~= '') then
message("Ошибка отправки транзакции: "..result, 1);
else
transaction_stage = "send";
end
while running do sleep(50); end
end
function OnTransReply(trans_reply)
if (tostring(trans_reply.trans_id) == transaction.TRANS_ID) then
if (trans_reply.brokerref ~= transaction.CLIENT_CODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится транзация?
set_stage("???"); -- прошу уточнить, что должно быть вместо ??? (в каком состоянии транзакция)
return;
end
if (trans_reply.class_code ~= transaction.CLASSCODE or trans_reply.sec_code ~= transaction.SECCODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится транзация?
set_stage("???");
return;
end
if (trans_reply.quantity ~= tonumber(transaction.QUANTITY)) then
-- Возможна ли такая ситуация, и в каком состоянии находится транзация?
-- Может ли транзакция выполниться частично? При каких условиях?
set_stage("???");
return;
end
-- какие коды (ret_code) могут быть в result_msg? где можно посмотреть весь список?
local ret_code = string.match(trans_reply.result_msg, "^%((%d+)%)");
-- в чём измеряется time? как привести время к текущему?
local time = trans_reply.time;
if (trans_reply.status == 3) then
transaction_details.order_num = trans_reply.order_num;
set_stage("Транзакция успешно отправлена на сервер в "..time.." с кодом сообщения "..ret_code);
else
set_stage("Ошибка отправки транзакции на сервер (время "..time..", код "..ret_code..")");
end
end
end
function OnTrade(order)
if (transaction_details.order_num == order.order_num) then
-- пожалуйста, прокомментируйте возможные значащие значения order.flags в этом месте
if (order.account ~= transaction.ACCOUNT) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.brokerref ~= transaction.CLIENT_CODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.class_code ~= transaction.CLASSCODE or order.sec_code ~= transaction.SECCODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.qty ~= tonumber(transaction.QUANTITY)) then
-- Возможна ли такая ситуация, и как будет выглядеть частичное выполнение транзакции на максимально доступный объём?
set_stage("???");
return;
end
transaction_details.price = order.price;
-- где ещё может использоваться trade_num в дальнейшем? при каких условиях? (пока не встречал этот id вне метода OnTrade)
transaction_details.trade_num = order.trade_num;
-- правильный ли тут статус?
set_stage(string.format("Совершается сделка %d на %s лотов %d по цене %s",
order.trade_num, bittest(order.flags, 2) and "продажу" or "покупку", order.qty, order.price))
end
end
function OnOrder(order)
if (transaction_details.order_num == order.order_num) then
-- те же проверки, что и в OnTrade
if (order.account ~= transaction.ACCOUNT) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.brokerref ~= transaction.CLIENT_CODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.class_code ~= transaction.CLASSCODE or order.sec_code ~= transaction.SECCODE) then
-- Возможна ли такая ситуация, и в каком состоянии находится сделка?
set_stage("???");
return;
end
if (order.qty ~= tonumber(transaction.QUANTITY)) then
-- Возможна ли такая ситуация, и как будет выглядеть частичное выполнение транзакции на максимально доступный объём?
set_stage("???");
return;
end
-- какие биты в order.flags в этом месте являются значащими?
-- что значит в этом месте установленный бит 4 в order.flags? (бит 4 (0x10) Разрешить / запретить сделки по разным ценам)
-- правильный ли тут статус?
set_stage(string.format("Заявка %d на %s лотов %d выполнена",
order.order_num, bittest(order.flags, 2) and "продажу" or "покупку", order.qty))
end
end
Ответы на многие вопросы проясняться, если Вы представите себе, что с помощью sendTransaction() с цикле отправляются несколько заявок по нескольким счетамиз нескольких разных скриптов одновременно, после чего каждый скрипт начинает получать в произвольном порядке коллбэки, причём каждый скрипт получает коллбэки от всего множества отправленных заявок.
Получится, что номера счетов могут быть разными, коды инструментов могут быть разными, какие-то заявки "свои для скрипта", а какие-то "чужие".
Дополнительно к этому добавляются специфические для QUIK проблемы типа несколько коллбэков OnOrder() подряд, в 7-й версии и OnTrade() тоже несколько для одной сделки, OnTransReply() может не придти при сетевых проблемах и т.д.
В общем, короче чем в 1000 строк удобный для последующего использования в роботах код для работы с заявками не получится.
Чтобы этот код написать, надо прочитать документацию, понять, как бороться с асинхронностью, разработать соответствующую модель. Это не просто, увы.
Ответы на многие вопросы проясняться, если Вы представите себе, что с помощью sendTransaction() с цикле отправляются несколько заявок по нескольким счетам из нескольких разных скриптов одновременно, после чего каждый скрипт начинает получать в произвольном порядке коллбэки, причём каждый скрипт получает коллбэки от всего множества отправленных заявок. Получится, что номера счетов могут быть разными, коды инструментов могут быть разными, какие-то заявки "свои для скрипта", а какие-то "чужие".
Как раз вопрос мой про это. Как отследить выполнение одной транзакции. В приведённом коде я уже выполняю необходимые проверки и от команды QUIK мне хотелось бы знать, достаточны ли они. Код заточен под то, что callback'и для одной транзакции приходят в порядке OnTransReply -> OnTrade -> OnOrder.
Может ли их порядок меняться местами - это тоже вопрос к команде QUIK. Про то, что OnOrder приходит дважды - я знаю, притом в таблице order поля не меняются. С двойным OnTrade не встречался, он приходит с разными таблицами order?
И если не придёт OnTransReply, разве не должен sendTransaction вернуть в данном случае ошибку?
Про ассинхронность - отдельный разговор. В приведённом мной примере только код после вызова sendTransaction в ф-ции main может выполняться параллельно. Всё остальное - синхронно, т.к. выполняется в главном потоке QUIK.
Жаль в документации нет этой информации по отслеживанию транзакций, поэтому и спрашиваю на форуме. В идеале хотелось бы создать Lua script, который генерирует одну или несколько транзакций и следит за их выполнением, обрабатывая ошибочные ситуации. Без участия команды QUIK эту проблему не решить, чтобы хоть как-то гарантировать, чтобы такой базовый робот продолжал работать и отчитываться об ошибках в течение дня и даже после обновления версии терминала.
Порядок OnTransReply -> OnTrade -> OnOrder обычно соблюдается, но может нарушаться. Программу надо писать так, чтобы она работала всегда. Вы ведь не хотите получить внезапно ошибку и убыток из-за неё.
Многократные ответы OnTrade() появились в 7-й версии. В повторных ответах могут добавиться некоторые поля.
Функция sendTransaction() может успешно выполниться, а OnTransReply() не придёт. Такое бывает, но очень редко. Однако, программу надо писать так, чтобы она работала всегда.
С асинхронностью нужно разобраться сразу и навсегда, чтобы не словить внезапных глюков. Один из способов такой: при получении коллбэка передавать данные через очередь из потока коллбэков в поток main(). В потоке main() периодически проверять очередь и обрабатывать данные, которые из неё приходят.
Проблему можно решить без участия команды QUIK. Надо прочитать документацию, разработать модель, запрограммировать её. Форум поможет при решении более тонких вопросов.
Основные шаги реализации примерно такие.
1) Нужны статусы заявок типа WAITING (ещё не отправлена), EXECUTING (отправлена на биржу), STOPPING (снимается, но не вся информация дошла), STOPPED (снята), EXECUTED (исполнена полностью), KILL_REJECTED (kill-заявка отвергнута), ERROR (ошибка).
2) Нужны таймауты для борьбы с неопознанными "посторонними" заявками (от других скриптов), а также заявками, для которых не пришёл OnTransReply().
3) Нужно следить за UID экземпляра QUIK, чтобы фильтровать "чужие" заявки.
4) Нужна функция генерации уникальных номеров транзакций, обеспечивающих непересекающиеся множества transId для разных скриптов.
5) Нужна таблица актуальных limit-заявок, куда заявка попадает при успешном вызове sendTransaction(), а удаляется при полном или частичном исполнении и ошибках.
6) Нужна таблица актуальных kill-заявок, куда заявка попадает при попытке снять limit-заявку, а удаляется при ошибках и исполнении.
7) Нужна таблица ответов о сделках на актуальные limit-заявки.
8) Нужен фильтр событий от "чужих" OnTrade(), например, работающий по комментарию к заявке.
9) В limit-заявке нужно помнить volume, volumeTraded, volumeLeft. Обычно volumeTraded + volumeLeft == volume, но иногда при снятии частично исполненной заявки становится понятно, чему равен volumeLeft и надо дождаться событий OnTrade(), которые ещё пока не пришли. При приходе OnTrade() нужно отбрасывать дубликаты (в 7-й версии) и корректировать volumeTraded, volumeLeft. Как только volumeLeft == 0, так удалять заявку из таблицы актуальных вместе со всеми связанными с ней kill-заявками и ответами на них.
10) При попытке снять limit-заявку иногда надо ждать, чтобы понять, какой order_num у отправленной заявки, поскольку ещё мог не придти ответ OnTransReply().
11) Периодически проверять, не стали ли известны order_num для лимитных заявок, для которых есть kill-заявки.
12) Периодически проверять, не стало ли известно, каким лимитным заявкам соответствуют OnTrade(), которые пришли раньше, чем OnTransReply().
13) Периодически разбираться с тем, сработали ли отправленные kill-заявки.
14) Удалять старые "неопознанные" сделки.
15) Реагировать на OnTransReply(), связывая transId скрипта и order_num биржи для limit-заявок. Понимать, как сработали kill-заявки. Проверять ждущие kill-заявки, если они есть.
16) Реагировать на OnTrade(), игнорируя дубликаты, логгируя соответствующие сделки.
17) Реагировать на OnOrder(), связывая transId скрипта и order_num биржи для limit-заявок. Понимать, что для некоторых уже неактивных заявок не дошли OnTrade() и логгировать сообщения об ошибках.
18) Периодически обрабатывать kill-заявки, чтобы понять, какие из них остались без OnTransReply(). Посылать новые, если что.
19) Периодически обрабатывать limit-заявки, чтобы понять, для каких из них получились кросс-сделки, а какие остались без OnTransReply(). Проверять, limit-заявки, которые почему-то неактивны, а volumeLeft не равен 0, и логгировать ошибки.
_sk_, да вы просто монстр, такое выдать! Утащу к себе. В п.6 можно поступить проще: не создавать отдельную таблицу, а добавить в таблицу ордера статус отправки команды на ее удаление, и в п.5 добавить - удаляется при снятии заявки.
_sk_, если будет время, пожалуйста, посмотрите на следующий скрипт. Он пока только умеет выставлять и отслеживать limit-заявки (т.е. без снятия/изменения). Хотелось бы понять, в правильном ли направлении я двигаюсь.
Пока не смог разобраться с пунктами
Цитата
3) Нужно следить за UID экземпляра QUIK, чтобы фильтровать "чужие" заявки.
Как получить свой uid? Что делать, если OnTransReply / OnOrder / OnStopOrder пришёл с другим uid? Правильно ли я понял, что под UID имеется ввиду поле uid, приходящее в OnTransReply и OnOrder?
Сам скрипт:
Код
Multimap = {};
Multimap.__index = Multimap;
function Multimap.Create()
local self = {};
setmetatable(self, Multimap);
return self;
end
function Multimap:Ins ert(key, value)
local list = self[key];
if (list == nil) then
list = {};
self[key] = list;
end
table.ins ert(list, value);
end
function Multimap:Remove(key, value)
local list = self[key];
assert(list ~= nil, "key not found");
for k,v in ipairs(list) do
if (v == value) then
table.remove(list, k);
if (#list == 0) then
self[key] = nil;
end
return;
end
end
assert(false, "val ue not found");
end
-- Transaction class
Transaction = {};
Transaction.__index = Transaction;
ST_PENDING_NEW = 1;
ST_REJECTED = 2;
ST_NEW = 3;
ST_FILLED = 4;
function Transaction.CreateLimit(request)
local self = {};
setmetatable(self, Transaction);
self.request = request;
local os_time = os.time();
self.id = self:GenerateId(os_time);
self.time = os_time;
self:ModifyHeader();
self.status = ST_PENDING_NEW;
Robot.transid_map[self.id] = self;
self.volume = tonumber(self.request.QUANTITY);
self.pending_volume = self.volume;
self.trades = {};
local ret = sendTransaction(self.request);
if (ret ~= '') then
Robot.transid_map[self.id] = nil;
self:OnStatusChanged(ST_REJECTED);
else
Robot.time_map:Insert(os_time, self);
end
end
function Transaction:ModifyHeader(os_time)
assert(self.request.TRANS_ID == nil, "We use trans_id internally.");
self.request.TRANS_ID = string.format("%d", self.id);
self.request.CLIENT_CODE = string.format("%s//L%04d", self.request.CLIENT_CODE, Robot.id);
end
Transaction.max_trans_id = 0;
function Transaction:GenerateId(os_time)
Transaction.max_trans_id = Transaction.max_trans_id + 1;
local id = Robot.id * 10000 + os_time % (60*60*24*30 * 300) + Transaction.max_trans_id;
assert(Robot.transid_map[id] == nil);
return id;
end
function Transaction:UpdateTiming()
Robot.time_map:Remove(self.time, self);
local os_time = os.time();
self.time = os_time;
Robot.time_map:Insert(os_time, self);
end
function Transaction:InternalDelete()
Robot.time_map:Remove(self.time, self);
Robot.transid_map[self.id] = nil;
if (self.orderid ~= nil) then
Robot.orderid_map[self.orderid] = nil;
end
end
-- consider we matched transaction by id and client_code already
function Transaction:OnTransReply(trans_reply)
if (self.flag_ontransreply) then
-- ignore all callback duplicates
return;
end
assert(trans_reply.class_code == self.request.CLASSCODE and trans_reply.sec_code == self.request.SECCODE, "classcode or seccode mismatch");
self.flag_ontransreply = true;
-- только status == 3 говорит об успешном выставлении заявки
if (trans_reply.status == 3) then
self:OnStatusChanged(ST_NEW);
if (self.orderid == nil) then
self.orderid = trans_reply.order_num;
Robot.orderid_map[self.orderid] = self;
else
assert(self.orderid == trans_reply.order_num);
end
self:UpdateTiming();
else
self:OnStatusChanged(ST_REJECTED);
self:InternalDelete();
end
end
-- consider we matched transaction by id and client_code already
function Transaction:OnOrder(order)
if (self.flag_onorder) then
-- ignore all callback duplicates
return;
end
assert(order.class_code == self.request.CLASSCODE and order.sec_code == self.request.SECCODE, "classcode or seccode mismatch");
self.flag_onorder = true;
if (self.orderid == nil) then
self.orderid = trans_reply.order_num;
Robot.orderid_map[self.orderid] = self;
else
assert(self.orderid == order.order_num);
end
local test = bittest(order.flags, 3);
if (test == 0) then
-- если бит 0 и 1 не установлены -- заявка выполнена
self:OnStatusChanged(ST_FILLED);
self:InternalDelete();
else
local sell = bittest(order.flags, 4) ~= 0;
assert((sell == (self.request.OPERATION == "S")) and (not sell == (self.request.OPERATION == "B")));
if (test == 2) then
self:OnStatusChanged(ST_REJECTED);
self:InternalDelete();
else
self:UpdateTiming();
end
end
end
-- consider we matched transaction by id and client_code already
function Transaction:OnTrade(order)
if (self.trades[order.trade_num]) then
-- ignore all callback duplicates
return;
end
assert(order.class_code == self.request.CLASSCODE and order.sec_code == self.request.SECCODE, "classcode or seccode mismatch");
self.trades[order.trade_num] = true;
print(string.format("OnTrade: %s:%s qty %g price %g settle %s:%s:%d",
order.class_code, order.sec_code, order.qty, order.price, order.settle_currency, order.settlecode, order.settle_date));
self.pending_volume = self.pending_volume - order.qty;
assert(self.pending_volume >= 0);
self:UpdateTiming();
end
function Transaction:OnStatusChanged(new_status)
message("Transaction changed status fr om "..self.status.." to "..new_status, 1);
self.status = new_status;
end
function Transaction:OnTimeout()
if (self.pending_volume == 0) then
self:InternalDelete();
else
self:UpdateTiming();
end
end
-- Robot class
Robot = {};
Robot.__index = {};
function Robot.Create(id, timeout)
assert(id ~= nil, "id is required");
timeout = timeout or 5;
assert(Robot.id == nil, "Robot is singleton class");
Robot.id = id;
Robot.timeout = timeout;
Robot.time_map = Multimap.Create();
Robot.transid_map = {};
Robot.orderid_map = {};
Robot.trade_queue = Multimap.Create();
return Robot;
end
function Robot:CheckTimeouts()
local limit_time = os.time() - Robot.timeout;
for timestamp,list in pairs(Robot.time_map) do
if (timestamp <= limit_time) then
for _,trans in ipairs(list) do
trans:OnTimeout();
end
end
end
-- check not matched yet transactions
for orderid,list in pairs(self.trade_queue) do
local trans = self.orderid_map[orderid];
if (trans ~= nil) then
self.trade_queue[orderid] = nil;
for _,v in ipairs(list) do
trans:OnTrade(v);
end
end
end
end
function Robot:CheckTradeQueueFor(trans)
local list = self.trade_queue[trans.orderid];
if (list ~= nil) then
self.trade_queue[trans.orderid] = nil;
for _,v in pairs(list) do
trans:OnTrade(v);
end
end
end
function Robot:CheckComment(comment)
return tonumber(string.match(comment, ".+//L(%d%d%d%d)$")) == self.id;
end
function Robot:SearchTransaction(transid, comment)
if comment and not self:CheckComment(comment) then
return nil;
end
if transid then
return self.transid_map[transid];
end
return nil;
end
function OnTransReply(trans_reply)
local trans = Robot:SearchTransaction(trans_reply.trans_id, nil, trans_reply.brokerref);
if (trans ~= nil) then
trans:OnTransReply(trans_reply);
end
end
function OnTrade(order)
if Robot:CheckComment(order.brokerref) then
local trans = Robot.orderid_map[order.order_num];
if (trans ~= nil) then
Robot:CheckTradeQueueFor(trans);
trans:OnTrade(order);
else
Robot.trade_queue:Insert(order.order_num, order);
end
end
end
function OnOrder(order)
local trans = Robot:SearchTransaction(order.trans_id, order.brokerref);
if (trans ~= nil) then
trans:OnOrder(order);
end
end
running = true;
function OnStop()
running = false;
end
function main()
print("Main function execution");
local robot = Robot.Create(4242);
Transaction.CreateLim it {
ACCOUNT = "L01-00000F00",
CLIENT_CODE = "51448",
ACTION = "NEW_ORDER",
CLASSCODE = "TQBR", SECCODE = "MTSS",
OPERATION = "S",
TYPE = "M", PRICE = "0", QUANTITY = "1",
};
while running do
sleep(50);
robot:CheckTimeouts();
end
end
-- Utilities
function bittest(val ue, mask)
local v = 1;
local v2 = 2;
local res = 0;
for i = 1,32 do
if (v > mask) then
break;
end
local m = mask % v2 / v >= 1;
local s = val ue % v2 / v >= 1;
if (m and s) then
res = res + v1;
end
v = v2;
v2 = v2 * 2;
end
return res;
end
log_file = io.open(getScriptPath().."\\robot.log", "a");
log_file:write("\n");
function print(...)
local r = { '[', os.date(), ']' };
local first = true;
for _,v in pairs({...}) do
if (first) then first = false; else r[#r+1] = '\t'; end
r[#r+1] = tostring(v);
end
r[#r+1] = '\n';
log_file:write(table.concat(r));
end
> Хотелось бы понять, в правильном ли направлении я двигаюсь
В правильном. Главное -- написать какой-то стартовый вариант, запустить его, скажем, на QUIK Junior и проверять логику. Отладчик Decoda поможет. Постепенно основные проблемы проявятся, а Вы поймёте, как надо делать.
Я бы обратил внимание на следующее (если правильно понял код; не могу сказать, что сильно детально разбирался).
1) Если флаги OnOrder говорят, что "заявка выполнена", нет гарантии, что все OnTrade() дошли (бывает и такое).
2) robot:CheckTimeouts() вызывается в потоке main(), OnOrder(), OnTrade(), OnTransReply() -- в потоке коллбэков; при этом они вызывают некоторые общие функции -- можно попасть на ошибки из-за многопоточности. Лучше передать всю информацию из потока коллбэков в поток main() с помощью очереди и уже там работать. Очередь на Lua, думаю, сами напишете без проблем.
Ну, и мелочи:
1) os.time() выдаёт ответ с точностью до секунд; возможно стоит прикрутить более точный способ измерения времени (я использую Socket и gettime())
2) В QUIK есть функция bit.band() для проверки флагов (см. документацию) с помощью проверок типа bit_band(t.flags, 4) == 4.
еще надо (я отслеживаю) заявки, которые выставляет человек, от заявок робота; заявки, которые являются стопами от заявок которые являются условными для открытия позиции. это до кучи
Здравствуйте. пишу простую форму для выставления заявок и автоматических выставлений стоп ордеров (замена стандартным F2 F6). инструмент один - фьюч на индекс, роботов нет. Человек когда надо вызывает форму, нажимает кнопку ок, открывается позиция на нужное количество контрактов и следом автоматом выставляется стоп. Вопрос следующий. если открывать позицию на большое количество контрактов (больше 1), ну скажем 200., часть контактов откроется по одной цене, часть по другой, часть по третей. следом после выполнения функции sendTransaction ко мне начнет приходить инфа про количество контрактов-цена открытия. где ее вылавливать? onTrade OnTransReply? фактически мне нужно получить trans_id (который в ответах будет один, как я понял?), количество-сумма. далее складываю количество контрактов и сравниваю с числом, которое указал пользователь в форме. если все сходится - делаю какие то вычисления и выставляю один стоп ордер на это количество
Интересная тема. Только истинные мастера могли в процессе отправки торгового приказа и получения рещультата его выполнения придумать столько гемороя.. я же всегда стремился к простоте. Поэтому задам простой вопрос.
По параметру ["TRANS_ID"] можно идентифицировать транзакцию в функции function OnTransReply(trans_reply) . Если trans_reply.status == 3, значит всё Ок. Если >3 значит ничего не получилось.. Теперь возникает вопрос с тем, что повторно отосланный на сервер торговый приказ будет иметь уже другой "TRANS_ID", обработка которого при нормальном (без отказов) выполнении всех транзакций, в функции OnTransReply будет совсем другой. Так вот вопрос: Можно ли послать повторный приказ на сервер с тем же "TRANS_ID", что и раннее неудавшийся? Тогда бот просто не заметит неудавшуюся тразакцию и будет обрабатывать реплаи по "TRANS_ID" требующейся в алгоритме операции, а не по порядковому номеру транзакции?
Роман Романов написал: Можно ли послать повторный приказ на сервер с тем же "TRANS_ID", что и раннее неудавшийся?
Да можно. Сервер никак не проверяет уникальность TRANS_ID Требование его уникальности относится только и только к функционалу динамического импорта транзакций из файла. В этом функционале, сам терминал QUIK проверяет уникальность TRANS_ID сравнивая его с теми что есть в tro файле.
Цитата
Роман Романов написал: Тогда бот просто не заметит неудавшуюся тразакцию и будет обрабатывать реплаи по "TRANS_ID" требующейся в алгоритме операции, а не по порядковому номеру транзакции?
Это уже зависит от того как написан этот самый бот.
Другая учетная запись, это может быть брокер, которого Вы попросили за Вас выставить заявку, или это у Вас несколько учетных записей. Учетная запись для WebQUIK (или любой другой терминал) и для QUIK может быть одна, а может быть для каждого терминала своя. Также для одного терминала может быть зарегистрировано несколько учетных записей, например мужа и жены.
Всем ПРИВЕТ и масса наилучших пожеланий. А вот и снова я со своими вопросами. Как всегда - не люблю создавать новых тем. Контент этой наиболее полно соответствует тому что меня интересует. (Спасибо автору топика и отдельное -- _sk_). Не все еще перерыл, но на свои вопросы ответа (готового), чувствую не найду. Поэтому пишу сюда. Есть куча собственных наработок. И в очередной раз нахлынула идея/желание/стремление как-то это всё наконец-то попытаться "монетизировать", говоря проще всё-таки -- написать собственного бота. Поскольку в сетапы торговых операций готов засунуть всё-что только можно, -- пришлось сделать собственный коннектор, на базе которого уже создан "скелет" робота. Сначала хотелось программные единицы "развязать" (бот и коннектор), чтобы отладку бота вести не в DLL, но исторически все "экспериментальные вещи" свалились в 1 кучу -- коннектор+бот (в одном флаконе), Определенное время (почти 2 года) этот "скелет" у меня пролежал "без мучений". За это время что-то поменялось в Квике, что-то нового узнал/от\крыл для себя (ну и кое-что также и забыл, как без этого). Бот делался как-бы на общественных началах (протестировать определенную идею, старую известную). Идея (в том, чтобы бот "кидал отложки") была не моя. Тогда меня привлекла мысль/идея (вполне, возможно, -- ошибочная), что мне его будет проще отлаживать (в том числе и на боевом счете, на котором денег нет, но исполнение стопов-то от того не зависит). Однако, "возникли мысли" и другие. А если лот -- не маленький (не 1 бумажка), и исполнится сразу не полностью, должна на остаток выставиться лимитка? А если стоп-лимит и тейк-профит (уже после покупающего стопа) настроены с отрицательным спредом, тоже будет лимитка? В общем, получается, что от "ведения сделки" никак не отвертеться? А еще есть определенные ограничения в заявке стоп-лимит и тейк-профит. А еще разные мысли по поводу нежелания светить свои стопы брокеру. А еще вот тут советы: http://www.bot4sale.ru/forum-menu/amisharp-forum/191-neskolko-voprosov-po-stopam-zayavkam-i-pozitsii... Встречал еще пару сайтов, источники для данного поста не сохранил. Если речь об HFT не идет . . . Насколько (и где, по каким причинам) нужны стопы (например, для быстрой покупки, если твой же брокер тебя по ним же и не "отлюбит")? Так же и с трейлингом сделки, может, "ну их в баню" стоп-лимит и тейк-профит. А правильнее свой алгоритм трейлинга написать. И даже защитный стоп обрабатывать самостоятельно (бот будет, так чтоб брокер его не видел). Вобщем, хотелось бы для себя, прежде чем в очередной раз "засучить рукава", разрешить, если можно так сказать, некоторые стратегические вопросы. Что посоветуют более старшие в программировании ботов товарищи? Обсуждение со стороны заказчиков ботов/потенциальных заказчиков также приветствуется. Особенно по вопросам не программной реализации, а общих вопросов видения того, как боты/стратегии должны работать. Прошу не стесняться.
Мой небольшой опыт. В своем роботе, когда фильтровал нужные ответы в onTransReply, мне приходилось фильтровать также по полю time, типа такого
Код
function TradeItem:handleTransReply(trans_reply)
....
if trans_reply.trans_id ~= self.trans_id or not trans_reply.time or trans_reply.time <= 0 then
return
end
....
end
Кроме того, при генерации trans_id, у меня в потоке всех заявок иногда случались одинаковые числа при использовании hh:mm:ss + math.random(1, 999), что меня порядком удивило и приводило к багам, поэтому пришлось проверять дополнительно:
Код
function TradeItem:generateTransId()
local resId
repeat
resId = tonumber(utils.getNormalizedTime() .. string.format("%03d", math.random(1, 999)))
until TradeItem.trans_ids[resId] == nil
return resId
end
Есть конечно множество других ньюансов, но о всех и не вспомнишь. Вообще весь механизм OnTransReply -> OnTrade -> OnOrder в QUIK'е (или на бирже) баганутый. Довольно редко, но transReply иногда просто не приходит. Приходится придумывать всякие воркараунды.
Просто смысл в том, что одно дело - это написать код, который по идее должен работать согласно документации, а другое дело - заставить его работать абсолютно всегда, без всяких фокусов раз в день - при большом потоке заявок. Второе довольно сложно.
Сергей написал: Приходится придумывать всякие воркараунды.
это является нормой в данном сегменте потребительской электроники семейства IBM PC AT-compatible уже как 30 лет. бо изначально мусорные технологии, ныне обернутые в блестящие фантики, своей сути не смогли и не сменили. чё уж там transReply глючит :)
Сергей Спасибо. Но мой вопрос-то был примерно такого плана. применять в роботе стопы или от них -- "больше зла"?
Цитата
Сергей написал: заставить его работать абсолютно всегда, без всяких фокусов раз в день - при большом потоке заявок.
Стопы (вред): 1. больше заявок, которые надо обрабатывать (больше потенциальных багов) 2. размещение на сервере у брокера, потенциально имеющего в своем арсенале алгоритм "собирания" этих самых стопов (не дай бог, если он еще и ММ) 3. усложнение алгоритма обработки вариативности событий Поясню. У меня необходимость применения стопов ассоциируется с "желанием" построить робота использующего необходимость реакции в быстродействующих процессах (не важно открытие позы или ее закрытие) На первый взгляд обрисовываются возможные проблемные вопросы: Например, исполнился покупающий СТОП, а лимитка от него исполнилась частично. Варианты обработки: 1 выставлять на исполненную часть СЛ и ТП (стоплосс и тейкпрофит) и ждать полного исполнения; 2 или ждать полного исполнения, до него СЛ и ТП НЕ выставлять 3 или на НЕисполненную часть заявку снимать и выставлять СЛ и ТП А еще пока снимали, заявка еще исполнилась (частично или полностью), -- в связи с чем алгоритм еще дополнительно "накручивается".
Или всё реализовать как в МетаТрейдере (где стопы реализуются на стороне терминала)? Чтобы по возможности избежать смешных ошибок, которые сейчас не видны и множества переделок.
В связи с чем хочется услышать.
Какие явные преимущества имеет робот, работающий именно "через" СТОП-приказы?
Если совсем не стесняться, то мне почему то сильно кажется, что на бирже нет никаких стоп-заявок. Только лимитки и заявки по рынку (не на фортс). Стоп-заявки реализованы на стороне брокера и им же обрабатываются. Таким образом, стоп-заявка - это алгоритм реализации выставления лимитной заявки, но решенный способами, разработанными квиковцами и на стороне сервера брокера. Поэтому, если рассматривать работу со стоп-заявками, то естественно, что на стороне брокера "они должны по идее/будут" исполняться быстрее, чем на стороне своего квика. Что лучше, подать лимитную заявку в систему или стоп-заявку, которая еще будет обрабатываться у брокера и потом попадет в систему - это каждый сам решает. При торговле большим количеством контрактов, действительно, нужно писать отдельный модуль управления заявками (что я и сделал для себя). Мой субъективный подход в том, чтобы не пользоваться стоп-заявками вообще. Это решает и проблему выявления стоп-заявок брокером (если у брокера действительно есть алгоритм собирания стопов, в чем я сильно сомневаюсь). Но в этом случае, алгоритм выставления лимитных заявок тоже приходится изобретать самому. Если торговля идет небольшим объемом, то проще, опять по моему субъективному мнению, посылать в систему рыночные заявки. P.S. вопрос на сообразительность. Если 5000 раз вызывать функцию math.random(1, 999), то какова вероятность получать одинаковые числа? Мне опять почему то сильно кажется, то вероятность будет равна 100%
Алексей Ч написал: Если 5000 раз вызывать функцию math.random(1, 999), то какова вероятность получать одинаковые числа? Мне опять почему то сильно кажется, то вероятность будет равна 100%
Ну так ведь 5000 в ~5 раз больше чем 999, так что да 100% будут повторения. И что в этом удивительного?
Алексей Ч написал: Если совсем не стесняться, то мне почему то сильно кажется, что на бирже нет никаких стоп-заявок. Только лимитки и заявки по рынку
а уж если совсем пойти в разнос, то так и до манулов недалеко опуститься ... :)
Цитата
2.3.1. Заявки – общие возможности Заявка — это приказ участника торгов в торговую систему на совершение сделки покупки или продажи инструмента по определён- ной цене. Заявка может быть адресной или безадресной. Безадресные заявки — это обычный вид заявок, которые встают в очередь и видны всем пользователям, они обязательно участву- ют в аукционе и сводятся со встречными заявками. Если у заявки есть контрпредложение с ценой лучшей или равной цене заявки, то такие заявки сводятся в сделку с ценой равной цене заявки в контрпредложении. Часть заявки, которая не свелась в сделку остается в виде заявки, с меньшим количеством инструмента. Заявки бывают котировочные, встречные и заявки Fill-or-Kill. Котировочная заявка остается в очереди независимо от того, свелась ли она частично, или не свелась совсем. Встречная заявка, если она не свелась в сделку, удаляется из системы после проведения аукциона. При частичном сведении встречной заявки, несведенная ее часть также удаляется. Заявки Fill-or-Kill — это встречные заявки, которые предполагают только полное исполнение (сведение в сделку). С точки зрения времени жизни заявки подразделяются на обычные и многодневные.
Цитата
5. Описание команд
Цитата
5.1. Метод FutAddOrder - Добавление заявки Тип сообщения: 412 Тип ответного сообщения: 101 Табл. 105. Входящие параметры
broker_code c4 "" isin c25 Код инструмента
client_code c3 Код клиента
type i4 Вид заявки
dir i4 Направления заявки
amount i4 Количество единиц инструмента
price c17 Цена заявки
comment c20 "" Поле комментария. Добавляется в заявку, сделку.
broker_to c20 "" Код РТС фирмы, которой адресована внесистемная заявка
ext_id i4 0 Внешний номер. Добавляется в заявку, сделку
du i4 0 Признак ДУ. Добавляется в заявку, сделку
date_exp c8 "" Дата истечения заявки. Добавляется в заявку.
hedge i4 0 Признак хэдж-заявки
dont_check_money i4 0 Признак расчета рисков по клиентскому разделу по данной
match_ref c10 "" Текст-связка для однозначного соответствия двух встречных адресных заявок
ncc_request i1 0 Признак запроса к НКЦ на заключение сделок с участником торгов
code i4 Код возврата
message c255 Текст сообщения
order_id i8 Код заявки в системе
Примечания: • Поле type может принимать следующие значения: 1 котировочная заявка (остаётся в очереди после частичного сведения) 2 встречная заявка (снимается после проведения аукциона) 3 заявка Fill-or-Kill
• Поле dir может принимать следующие значения: 1 заявка на покупку 2 заявка на продажу
• В поле price задаётся цена заявки в строковом виде 'nnnnnnnnnn.mmmmm'.
У-у-ффф. Всё самое лучшее для программера -- это то, что нашел или сделал САМ.
https://smart-lab.ru/blog/309922.php Неужели всё -- ТАК? (Знак вопроса долго не ставился как и переключение рус/лат, наверное это -- ЗНАК . . . )) . . . .)
"Всем -- Спасибо, Все -- свободны . . ."
Но если мнения есть, думаю, все с удовольствием почитают.
Алексей Ч написал: Если 5000 раз вызывать функцию math.random(1, 999), то какова вероятность получать одинаковые числа? Мне опять почему то сильно кажется, то вероятность будет равна 100%
Ну так ведь 5000 в ~5 раз больше чем 999, так что да 100% будут повторения. И что в этом удивительного?
для меня ничего удивительно нет, все так. Удивительно это для того, то применял этот метод несколькими постами выше моего поста.
Удивительная это вещь: общение на форумах! Спрашивают совет про стоп-заявки. Пишу, что стоп-заявок нет, используете лимитки. Один меня (зачем-то) тычет носом в мануал, где написано, что стоп-заявок нет. А вопрошающий вдруг сам рад тому, что где то нашел другой тот же самый совет использовать лимитки.
PFelix, Стопы хранятся на сервере брокера и их обрабатывает непосредственно сервер QUIK по поступающей с биржи информации. Таким образом, если забыть про то зачем в принципе созданы стопы, то по сравнению с лимитками они лучше тем что транзакция уходит до биржи сразу с сервера QUIK. Например это полезно как раз если канал от терминала до брокера слишком медленный. Если интересуют какие-то другие аспекты "обработки" готовы ответить на конкретные вопросы.
новичок, не понял, зачем ты мне ссылку на чикагский мануал прислал? Давай еще на китайском найди чтоли. Или лучше место, где в мануале от плазы про стопы написано. P.s. Хамство - не признак большого ума
Алексей Ч написал: новичок , не понял, зачем ты мне ссылку на чикагский мануал прислал? Давай еще на китайском найди чтоли. Или лучше место, где в мануале от плазы про стопы написано. P.s. Хамство - не признак большого ума
это не тебе, чудик. с тобой всё понятно. кому надо - сделают выводы. со временем.
Сергей, вопрос ЕСТЬ. Однажды столкнулся с такой вот "неожиданностью". Мой брокер (очень близкий к вершине по рейтингу, разным рейтингам) не транслировал отдельные параметры, по отдельным инструментам, далее (ВНИМАНИЕ) на отдельном из своих СЕРВЕРОВ. Вопрос такой. Есть ли в "арсенале" QLUA нечто специально "заточненное", чтобы программно "понять", что "данный" сервер какие-то нужные скрипту "параметры" НЕ транслирует?
PFelix написал: Есть ли в "арсенале" QLUA нечто специально "заточненное", чтобы программно "понять", что "данный" сервер какие-то нужные скрипту "параметры" НЕ транслирует?
такой возможности нет. Можно толь проверить получает ли скрипт данные или нет. Если не получает значит что то не так.
PFelix написал: Если есть, -- Где такие заявки "хранятся" (брокер / биржа)?
В QUIK есть свои собственные айсберг заявки, это отдельный алго-модуль Они хранятся на сервере QUIK И они работают как с ФОРТС так и с другими рынками.