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 парсер (стандартный парсер Делфи)
if(Root.NodeName = 'quotes') then //рутовый тег сообщения
begin
if(Root.ChildNodes.Count > 0) then
begin
for i:= 0 to Root.ChildNodes.Count - 1do
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;
tryfor j:= 0 to Length(inStor.InstrHdr) - 1do
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;
tryif(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;
tryfor j:= 0 to ChildNode.ChildNodes.Count - 1do
begin
NChildNode:= ChildNode.ChildNodes[j];
if(NChildNode.NodeName = 'board') then
inStor.Instruments[Position].Stakan[i].board:= NChildNode.Text elseif(NChildNode.NodeName = 'seccode') then
inStor.Instruments[Position].Stakan[i].seccode:= NChildNode.Text elseif(NChildNode.NodeName = 'price') then
TryStrToFloat(NChildNode.Text, inStor.Instruments[Position].Stakan[i].price) elseif(NChildNode.NodeName = 'source') then
inStor.Instruments[Position].Stakan[i].source:= NChildNode.Text elseif(NChildNode.NodeName = 'yield') then
TryStrToInt(NChildNode.Text, inStor.Instruments[Position].Stakan[i].yield) elseif(NChildNode.NodeName = 'buy') then
TryStrToInt(NChildNode.Text, inStor.Instruments[Position].Stakan[i].buy) elseif(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
Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!
я не знаю устройства вашего внутреннего устройства :-) Как вы там всё организовали
почему получилось что приёмник ровно один и такой нагруженный. Если сетевое соединение возможно только одно, значит он должен раскидывать приходящие данные по исполнителям (на основе инструмента например).
образно - приёмник получил сообщение "изменения стакана по EURUSD" и должен сразу скинуть в тред который отвечает за EURUSD. Или если возможны несколько подключений, то открывать несколько и подписываться на EURUSD по одному а на GBPJPY по другому.
но это вам скорее не грозит - запустите работу по 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;
А так же функция проверки работы сервиса '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);
tryif (h_manager > 0) then
begin
//--- Open service with status
h_service:= OpenService(h_manager, sName, SERVICE_QUERY_STATUS);
tryif (h_service > 0) then
begin
//--- Get Service statusif(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;
В моих роботах нужен стакан хотя бы с 3-я sell и с3-я buy
В том-то и проблема, что я торгую по всем инструментам ФОРТС, а их на реале около 500.
Ну, обновить 3000 значений даже с сортировкой О(N) никак не 30мс - на 3 порядка быстрей, думаю на современных процессорах.
Проблема в организации XML - сообщений
Попробую подробно объяснить
Структура хранилища данных выглядит так
seccode + board - Эта пара позволяет идентифицировать инструмент
А теперь цепочка обработки приходящих сообщений
1. Приходящее сообщение помещается в XML парсер (стандартный парсер Делфи)
2. Парсер ищет рутовый тег сообщения
3. Поиск пары, для идентификации инструмента
4. Первое заполнение стакана
5. Обновление стакана
Так вот, каждую измененную запись нужно найти и отсортировать, иначе
стакан не будет агрегирован! А из поста выше, видно сколько изменений нужно обработать!
Комп просто будет висеть, даже на 20 инструментах.
Добавлено
С списте инструментов ФОРТС - 13816 инструментов, для этого создан Instruments header
В моих роботах нужен стакан хотя бы с 3-я sell и с3-я buy
В том-то и проблема, что я торгую по всем инструментам ФОРТС, а их на реале около 500.
заведите пул по числу свободных тредов, пусть разбирают поступающее и накладывают изменения
в однопотоке с приемлемой скоростью разбираются скриптами 5-10 стаканов.
Для скальперных скальперов - один поток на скальпер, и потоки пополам (должны быть свободные). им дико скорость нужна
заведите пул по числу свободных тредов, пусть разбирают поступающее и накладывают изменения
в однопотоке с приемлемой скоростью разбираются скриптами 5-10 стаканов.
Для скальперных скальперов - один поток на скальпер, и потоки пополам (должны быть свободные). им дико скорость нужна
Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!
Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!
я не знаю устройства вашего внутреннего устройства :-) Как вы там всё организовали
почему получилось что приёмник ровно один и такой нагруженный. Если сетевое соединение возможно только одно, значит он должен раскидывать приходящие данные по исполнителям (на основе инструмента например).
образно - приёмник получил сообщение "изменения стакана по EURUSD" и должен сразу скинуть в тред который отвечает за EURUSD. Или если возможны несколько подключений, то открывать несколько и подписываться на EURUSD по одному а на GBPJPY по другому.
но это вам скорее не грозит - запустите работу по 2-м инструментам с максимально возможным стаканом и все косяки и недочёты сами всплывут. И будет ясность как исправлять на текущей архитектуре или как её поменять.
Приемник сообщений только один, чтобы направить сообщение нужно его идентифицировать!
В принципе, согласен с тем, что хмл - не лучший вариант для таких вещей. Поэтому этот вариант сразу пропускаю, займусь плазой.
Пора выходить, так сказать, на новый уровень.
В принципе, согласен с тем, что хмл - не лучший вариант для таких вещей. Поэтому этот вариант сразу пропускаю, займусь плазой.
Пора выходить, так сказать, на новый уровень.
Я об этом лет 8 назад говорил....
Но там, на плазе, конечно, все повеселее, но стаканы обновляются по тому же принципу.
Кстати, я забросил Tranreq connector и переключился тоже на Плазу.
Будет, что обсудить... :)
Добавлено
Вам хорошо, подцепил Cgate.h и все, а мне весь нужно "перестукивать" :(
Для Плаза 2 понадобятся следующие функции:
Ключ реестра = 'SOFTWARE\Moscow Exchange\SPECTRA Client''s Gate'
Запись = 'InstallDir'
А так же функция проверки работы сервиса 'SpectraCGate'
Если сервис не работает, то загружаем роутер ручками...
Командная строка выглядит так:
А роутер без наличия окна точно работает нормально? Проверялось? Я пока не в курсах.
Окно будет, но только в "невидимом" режиме, я проверял, все ок
Движется потихонечку