Порядок отслеживания процесса выполнения транзакций

Страницы: 1 2 След.
RSS
Порядок отслеживания процесса выполнения транзакций
 
Пожалуйста, помогите удостоверится в правильности отслеживания выполнения транзакций в 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

 
Прошу простить. Код для bittest в предыдущем сообщении оказался неверен. Вот правильный вариант.
Код
function bittest(number, shift)
    local v = 1;
    for i=1,shift do
        v = v * 2;
    end
    return number / v % 2 >= 1;
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 добавить - удаляется при снятии заявки.
Lbot3D
 
_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
 
Цитата
Как получить свой uid?
В заголовке QUIK, когда подключились к серверу
 
swerg, прошу прощения, забыл уточнить.
Цитата
Как получить свой UID из Lua?
Этот вопрос уже решил.
Код
GetInfoParam("USERID")
 
> Хотелось бы понять, в правильном ли направлении я двигаюсь

В правильном. Главное -- написать какой-то стартовый вариант, запустить его, скажем, на 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 (который в ответах будет один, как я понял?), количество-сумма. далее складываю количество контрактов и сравниваю с числом, которое указал пользователь в форме. если все сходится - делаю какие то вычисления и выставляю один стоп ордер на это количество
 
onTrade
проще смотреть в таблице лимитов там уже все сложено
 
Цитата
Николай Камынин написал:
проще смотреть в таблице лимитов там уже все сложено
Дык, сделки и лимиты обновляются не синхронно.
 
К тому же лимиты могут измениться и без сделок.
 
Интересная тема. Только истинные мастера могли в процессе отправки торгового приказа и получения рещультата его выполнения придумать столько гемороя..
я же всегда стремился к  простоте. Поэтому задам простой вопрос.

По параметру    ["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" требующейся в алгоритме операции, а не по порядковому номеру транзакции?
Это уже зависит от того как написан этот самый бот.
 
Цитата
_sk_ написал:
3) Нужно следить за UID экземпляра QUIK, чтобы фильтровать "чужие" заявки.
Насколько "чужие" эти "чужие" заявки?  Из других терминалов того же пользователя (типа WebQuik)?  Или еще какие-то случаи бывают?
 
kroki,
Если на заявке не Ваш UID, значит она была выставлена с другой учетной записи.
И терминал тут не имеет значения (WebQUIK или любой другой https://arqatech.com/ru/products/quik/terminals/user-applications/)

Другая учетная запись, это может быть брокер, которого Вы попросили за Вас выставить заявку, или это у Вас несколько учетных записей.
Учетная запись для 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'.
 
Цитата
новичок написал:
Заявки Fill-or-Kill — это встречные заявки, которые предполагают только полное исполнение (сведение в сделку).
Видел такие в ордерлоге.
Странное дело. Флага встречной там НЕТ.
А по ценам в заявке и сделке -- явно с претензией на встречную.
Х-м. Будем ЗНАТЬ.
 
У-у-ффф. Всё самое лучшее для программера -- это то, что нашел или сделал САМ.

https://smart-lab.ru/blog/309922.php
Неужели всё -- ТАК?
(Знак вопроса долго не ставился как и переключение рус/лат, наверное это -- ЗНАК . . . )) . . . .)

"Всем -- Спасибо, Все -- свободны . . ."

Но если мнения есть, думаю, все с удовольствием почитают.
 
Цитата
а уж если совсем пойти в разнос, то так и до манулов недалеко опуститься ... :)
новичок, т.е. стоп-заявок на самой бирже нет, как я и предполагал. Спасибо
 
Цитата
Алексей Ч написал:
стоп-заявок на самой бирже нет
Это-то -- "вещь" известная и ни кем не скрываемая.
 
Цитата
Sergey Gorokhov написал:
Цитата
Алексей Ч написал:
Если 5000 раз вызывать функцию math.random(1, 999), то какова вероятность получать одинаковые числа? Мне опять почему то сильно кажется, то вероятность будет равна 100%
Ну так ведь 5000 в ~5 раз больше чем 999, так что да 100% будут повторения. И что в этом удивительного?
для меня ничего удивительно нет, все так. Удивительно это для того, то применял этот метод несколькими постами выше моего поста.

Удивительная это вещь: общение на форумах! Спрашивают совет про стоп-заявки. Пишу, что стоп-заявок нет, используете лимитки. Один меня (зачем-то) тычет носом в мануал, где написано, что стоп-заявок нет. А вопрошающий вдруг сам рад тому, что где то нашел другой тот же самый совет использовать лимитки.  :shock:  
 
Цитата
PFelix написал:
Какие явные преимущества имеет робот, работающий именно "через" СТОП-приказы?


Цитата
Алексей Ч написал:
стоп-заявок нет

Я не спрашивал того, где могут быть (ожидая на бирже) выставлены мои стопы.
У меня, знаете ли,  пинг до брокера --    230-270.
География, "паммаешь ли".

Меня интересовало не место, а преимущества/вред от СТОПОВ  ДЛЯ БОТОВ.
Цитата
Алексей Ч написал:
топ-заявки реализованы на стороне брокера и им же обрабатываются
Меня интересует, Честно ли ЭТА ОБРАБОТКА происходит.
И меня, разумеется, не интересуют рейтинги брокеров, составленные брокерами или ММВБ.

Ваша чукча, нака, сразу -- пЫсатель,   нака,     нИРазу -- не читатель.

"Прошу понять".
 
PFelix,
Стопы хранятся на сервере брокера и их обрабатывает непосредственно сервер QUIK по поступающей с биржи информации.
Таким образом, если забыть про то зачем в принципе созданы стопы, то по сравнению с лимитками они лучше тем что транзакция уходит до биржи сразу с сервера QUIK.
Например это полезно как раз если канал от терминала до брокера слишком медленный.
Если интересуют какие-то другие аспекты "обработки" готовы ответить на конкретные вопросы.
 
Цитата
Алексей Ч написал:
Пишу, что стоп-заявок нет, используете лимитки. Один меня (зачем-то) тычет носом в мануал, где написано, что стоп-заявок нет.
вовсе не это там написано, почитай таки на досуге еслиф чо ...
Цитата
(зачем-то) тычет носом в мануал,
https://www.cmegroup.com/confluence/display/EPICSANDBOX/iLink+Order+Types

ну вот опять "потычу" ... ничо не замечаешь , э .. э?
Цитата
Алексей Ч написал:
дивительная это вещь: общение на форумах!
это единственное, что ты тут точно угадал ... и то потому (паходу), что сам организовал  сии чудеса ... :)

расея-расея :)
 
новичок, не понял, зачем ты мне ссылку на чикагский мануал прислал? Давай еще на китайском найди чтоли. Или лучше место, где в мануале от плазы про стопы написано.
P.s. Хамство - не признак большого ума
 
Цитата
Алексей Ч написал:
новичок , не понял, зачем ты мне ссылку на чикагский мануал прислал? Давай еще на китайском найди чтоли. Или лучше место, где в мануале от плазы про стопы написано.
P.s. Хамство - не признак большого ума
это не тебе, чудик. с тобой всё понятно.
кому надо - сделают выводы. со временем.
 
Цитата
Алексей Ч написал:
P.s. Хамство - не признак большого ума
Спасибо, он не пАймёт, он -- нАвичок (такие синие, с хвостами)
 
Сергей, вопрос ЕСТЬ.
Однажды столкнулся с такой вот "неожиданностью".
Мой брокер (очень близкий к вершине по рейтингу, разным рейтингам) не транслировал отдельные параметры, по отдельным инструментам,
далее (ВНИМАНИЕ) на отдельном из своих СЕРВЕРОВ.
Вопрос такой.
Есть ли в "арсенале" QLUA нечто специально "заточненное", чтобы программно "понять", что "данный" сервер какие-то нужные скрипту "параметры" НЕ транслирует?
 
Блин, наблюдаю,  . . . ) долго думает, дОлго пишет, чувствую, сейчас  . . .   "на  В  ишет".   ))
 
Мне неприятно это констатировать, НО уже минут 10 "ПИШЕТ" . . .  
 
Сергей, вопрос к ВАМ.
С учетом того что написано на:
http://forum.moex.com/viewtopic.asp?t=21226&topicdays=0&postorder=asc&start=60

ПРИЗНАЮСЬ ЧЕСТНО, . . .
полностью не перечитал.
Есть такое на FORTS?
Если есть,  -- Где такие заявки "хранятся" (брокер / биржа)?
 
Цитата
PFelix написал:
Есть ли в "арсенале" QLUA нечто специально "заточненное", чтобы программно "понять", что "данный" сервер какие-то нужные скрипту "параметры" НЕ транслирует?
такой возможности нет.
Можно толь проверить получает ли скрипт данные или нет.
Если не получает значит что то не так.
 
Цитата
PFelix написал:
Есть такое на FORTS?
Это вопрос к биржевому протоколу.
 
Цитата
PFelix написал:
Если есть,  -- Где такие заявки "хранятся" (брокер / биржа)?

В QUIK есть свои собственные айсберг заявки, это отдельный алго-модуль
Они хранятся на сервере QUIK
И они работают как с ФОРТС так и с другими рынками.
 
Цитата
PFelix написал:
полностью не перечитал.

На бирже по ФР и ВР есть биржевые айсберги, они поддерживаются в QUIK, если их у Вас нет, надо просить брокера.
 
Сергей, спасибо за развёрнутый ответ.
Всё понятно.
 
И, я правильно понимаю, что калбеков  OnOrder (например) при исполнении такой заявки будет тьма,
НО    linkedorder у  них будет -- единым?
Страницы: 1 2 След.
Читают тему (гостей: 1)
Наверх