Нужна помощь профессионального программиста - страница 13

 
Dmitriy Skub #:
А зачем Вам весь стакан формировать все время по всем символам? Для алгоритма это не нужно. Для визуального отображения тоже.

В моих роботах нужен стакан хотя бы с 3-я sell и с3-я buy

В том-то и проблема, что я торгую по всем инструментам ФОРТС, а их на реале около 500.  

 
prostotrader #:

В моих роботах нужен стакан хотя бы с 3-я sell и с3-я buy

В том-то и проблема, что я торгую по всем инструментам ФОРТС, а их на реале около 500.  

Ну, обновить 3000 значений даже с сортировкой О(N) никак не 30мс - на 3 порядка быстрей, думаю на современных процессорах.
 
Dmitriy Skub #:
Ну, обновить 3000 значений даже с сортировкой О(N) никак не 30мс - на 3 порядка быстрей, думаю на современных процессорах.

Проблема в организации XML - сообщений

Попробую подробно объяснить

Структура хранилища данных выглядит так

//--- Storage ---                 
  TTRQStorage = packed record
    Accaunts: array of TAccaunt;
    Markets: array of TMarket;
    CurMarket: integer;
    InstrHdr: array of TInstrumentHeader; // Указатель позиции инструмента в списке инструментов
    Instruments: array of TInstrument;    // Список инструментов
    Boards: array of TBoard;
    CurBoard: string;
    Candlekinds: array of TKind;
    Money: TPositions;
    Messages: array of TMessage;
    SecInfoUpd: array of TSecuritiUpdate;  //Temp buffer
  end;
//--- Instruments header ---
  TInstrumentsHeader = packed record
    Name: string;
    SecCode: string;
    Market: integer;
    Board: string;
    insPos: integer;  // Номер позиции инструмента в общем списке
  end;
//--- Instrument ---
  TInstrument = packed record
    Securiti: TSecuriti;                  //Описание инструмента
    InstrInfo: TSecuritiInfo;           //информация по инструменту
    Pit: TPit;
    Stakan: array of TQuote;        // Стакан
    Quant: TQuotation;
    OutBuff: TDataBuffer;
  end;
//--- Глубина рынка по инструменту(ам) ---
  TQuote = packed record
    secid: integer;
    board: string;                           // идентификатор борда
    seccode: string;                       //  код инструмента
    price: double;
    source: string;
    yield: integer;
    buy: integer;
    sell: integer;
  end;

seccode + board - Эта пара позволяет идентифицировать инструмент

А теперь цепочка обработки приходящих сообщений

1. Приходящее сообщение помещается в XML парсер (стандартный парсер Делфи)

ParentComp:= TComponent.Create(nil);
  try
    CoInitializeEx(Nil, COINIT_MULTITHREADED);
    XML:= TXMLDocument.Create(ParentComp);
    try
     // XML.LoadFromFile('Test.xml');    //Debug
      XML.LoadFromXML(UTF8ToString(inData));
      XML.Encoding:= 'ANSI';
      Xml.Active:= true;
..............................................

2. Парсер ищет рутовый тег сообщения

3. Поиск пары, для идентификации инструмента

4. Первое заполнение стакана

5. Обновление стакана 

if(Root.NodeName = 'quotes') then                               //рутовый тег сообщения
        begin
          if(Root.ChildNodes.Count > 0) then
          begin
            for i:= 0 to Root.ChildNodes.Count - 1 do
            begin
              ChildNode:= Root.ChildNodes[i];
              if(ChildNode.NodeName = 'quote') then                       // поиск пары
              begin
                BoardNode:= ChildNode.ChildNodes.FindNode('board');
                SecCodeNode:= ChildNode.ChildNodes.FindNode('seccode');
                if((BoardNode <> nil) and (SecCodeNode <> nil)) then
                begin
                  Position:= -1;                                          // определение позиции инструмента в списке инструментов  
                  Mutex.Lock;
                  try
                    for j:= 0 to Length(inStor.InstrHdr) - 1 do
                    begin
                      if((BoardNode.Text = inStor.InstrHdr[j].Board) and
                         (SecCodeNode.Text = inStor.InstrHdr[j].SecCode)) then
                      begin
                        Position:= inStor.InstrHdr[j].insPos;                     // нашли
                        break;
                      end;
                    end;
                  finally
                    Mutex.Unlock;
                  end;
                  if(Position = -1) then Continue;                               // не нашли 
                  if(length(inStor.Instruments[Position].Stakan) = 0) then
                  begin
                    Mutex.Lock;
                    try
                      SetLength(inStor.Instruments[Position].Stakan, Root.ChildNodes.Count); // установка размера стакана
                    finally
                      Mutex.UnLock;
                    end;
                  end;
                  if(Root.ChildNodes.Count = 100) then                                     // Первое заполнение стакана
                  begin
                    if(ChildNode.AttributeNodes.Count > 0) then
                    begin
                      AttrNode:= ChildNode.AttributeNodes[0];
                      Mutex.Lock;
                      try
                        if(AttrNode.NodeName = 'secid') then
                          TryStrToInt(AttrNode.Text, inStor.Instruments[Position].Stakan[i].secid);
                      finally
                        Mutex.UnLock;
                      end;
                    end;
                    if(ChildNode.ChildNodes.Count > 0) then
                    begin
                      Mutex.Lock;
                      try
                        for j:= 0 to ChildNode.ChildNodes.Count - 1 do
                        begin
                          NChildNode:= ChildNode.ChildNodes[j];
                          if(NChildNode.NodeName = 'board') then
                            inStor.Instruments[Position].Stakan[i].board:= NChildNode.Text else
                          if(NChildNode.NodeName = 'seccode') then
                            inStor.Instruments[Position].Stakan[i].seccode:= NChildNode.Text else
                          if(NChildNode.NodeName = 'price') then
                            TryStrToFloat(NChildNode.Text, inStor.Instruments[Position].Stakan[i].price) else
                          if(NChildNode.NodeName = 'source') then
                            inStor.Instruments[Position].Stakan[i].source:= NChildNode.Text else
                          if(NChildNode.NodeName = 'yield') then
                            TryStrToInt(NChildNode.Text, inStor.Instruments[Position].Stakan[i].yield) else
                          if(NChildNode.NodeName = 'buy') then
                            TryStrToInt(NChildNode.Text, inStor.Instruments[Position].Stakan[i].buy) else
                          if(NChildNode.NodeName = 'sell') then
                            TryStrToInt(NChildNode.Text, inStor.Instruments[Position].Stakan[i].sell);
                        end;
                      finally
                        Mutex.Unlock;
                      end;
                    end;
                  end else
                  begin
                    if(Length(inData) = 0) then;
                    //TODO Update stakan ????????????????????????????
                  end;
                end;
              end;
            end;
          end;
        end;


 Так вот, каждую измененную запись нужно найти и отсортировать, иначе

стакан не будет агрегирован! А из поста выше, видно сколько изменений нужно обработать!


Комп просто будет висеть, даже на 20 инструментах.

Добавлено

С списте инструментов ФОРТС - 13816 инструментов, для этого создан Instruments header

 
prostotrader #:

В моих роботах нужен стакан хотя бы с 3-я sell и с3-я buy

В том-то и проблема, что я торгую по всем инструментам ФОРТС, а их на реале около 500.  

заведите пул по числу свободных тредов, пусть разбирают поступающее и накладывают изменения

в однопотоке с приемлемой скоростью разбираются скриптами 5-10 стаканов. 

Для скальперных скальперов - один поток на скальпер, и потоки пополам (должны быть свободные). им дико скорость нужна

 
Maxim Kuznetsov #:

заведите пул по числу свободных тредов, пусть разбирают поступающее и накладывают изменения

в однопотоке с приемлемой скоростью разбираются скриптами 5-10 стаканов. 

Для скальперных скальперов - один поток на скальпер, и потоки пополам (должны быть свободные). им дико скорость нужна

Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!

 
prostotrader #:

Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!

я не знаю устройства вашего внутреннего устройства :-) Как вы там всё организовали

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

образно - приёмник получил сообщение "изменения стакана по EURUSD" и должен сразу скинуть в тред который отвечает за EURUSD. Или если возможны несколько подключений, то открывать несколько и подписываться на EURUSD по одному а на GBPJPY по другому. 

но это вам скорее не грозит - запустите работу по 2-м инструментам с максимально возможным стаканом и все косяки и недочёты сами всплывут. И будет ясность как исправлять на текущей архитектуре или как её поменять.

 
prostotrader #:

Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!

В принципе, согласен с тем, что хмл - не лучший вариант для таких вещей. Поэтому этот вариант сразу пропускаю, займусь плазой.

Пора выходить, так сказать, на новый уровень.

 
Dmitriy Skub #:

В принципе, согласен с тем, что хмл - не лучший вариант для таких вещей. Поэтому этот вариант сразу пропускаю, займусь плазой.

Пора выходить, так сказать, на новый уровень.

Я об этом лет 8 назад говорил....

Но там, на плазе, конечно, все повеселее, но стаканы обновляются по тому же принципу.

Кстати, я забросил Tranreq connector и переключился тоже на Плазу.


Будет, что обсудить... :)

Добавлено

Вам хорошо, подцепил Cgate.h и все, а мне весь нужно "перестукивать" :(


 

Для Плаза 2 понадобятся следующие функции:

//--- Get cgate lib fullpath
function GetCgDllFullPath;
var
 Reg: TRegistry;
 k: integer;
begin
  result:= '';
  Reg:= TRegistry.Create;
  try
    Reg.RootKey:= HKEY_LOCAL_MACHINE;
    if(Reg.OpenKeyReadOnly(regKey)) then
    begin
      result:= Reg.ReadString(regValue);
      if(result <> '') then
      begin
        k:= Pos('bin', result);
        if (k = 0) then result:= result + 'bin\';
      end;
    end;
  finally
    Reg.Free;
  end;
end;

Ключ реестра = 'SOFTWARE\Moscow Exchange\SPECTRA Client''s Gate'

Запись = 'InstallDir'

А так же функция проверки работы сервиса 'SpectraCGate'

function TCGConnector.GetCgService(const Machine: PWideChar; const sName: PWideChar): boolean;
var
  h_manager, h_service: SC_Handle;
  lpServiceStatus: SERVICE_STATUS;
begin
  result:= false;
//--- Open service manager
  h_manager:= OpenSCManager(Machine, nil, SC_MANAGER_CONNECT);
  try
    if (h_manager > 0) then
    begin
//--- Open service with status
      h_service:= OpenService(h_manager, sName, SERVICE_QUERY_STATUS);
      try
        if (h_service > 0) then
        begin
//--- Get Service status
          if(QueryServiceStatus(h_service, lpServiceStatus)) then
            if(lpServiceStatus.dwCurrentState = SERVICE_RUNNING) then
              result:= true;
        end;
      finally
        CloseServiceHandle(h_service);
      end;
    end;
  finally
    CloseServiceHandle(h_manager);
  end;
end;

Если сервис не работает, то загружаем роутер ручками...

function TCGConnector.LoadRouter(const FileName: String): Boolean;
var
  StartInfo: TStartupInfo;
  CmdLine: String;
  RPath: string;
  k: integer;
begin
  result:= false;
  RPath:= ExtractFilePath(Filename);
  k:= Pos('bin\', RPath);
  if (k > 0) then
  begin
   Delete(RPath, k, Length('bin\'));
   RPath:= '"' + RPath + 'CLIENT_router.ini"';
  end;
  CmdLine:= '"' + Filename + '" ' + '/ini:' + RPath;
  FillChar(StartInfo, SizeOf(StartInfo), 0);
  with StartInfo do
  begin
    cb:= SizeOf(StartInfo);
    wShowWindow := SW_SHOWNORMAL;
  end;
  Result:= CreateProcess(Nil, PWideChar(CmdLine), Nil, Nil, false,
                         NORMAL_PRIORITY_CLASS{ or CREATE_NO_WINDOW}, Nil, // DEBUG!!! view window
                         PWideChar(ExtractFilePath(Filename)), StartInfo, FProcInfo);
end;

Командная строка выглядит так:


 
А роутер без наличия окна точно работает нормально? Проверялось? Я пока не в курсах.
Причина обращения: