← Назад

Управление стаканом котировок

Добрый день, Михаил! Как-то заметил, что стакан котировок отображается не совсем правильно - цена почему-то не снижается сверху вниз по всей глубине, а начинается с мимнимальной котировки и повышается сверху вниз по "биду", а в месте где начинается "аск" "переворачивается и снова начинается с самой маленькой котировки "аск" и так повышается до самого низа. Думал отсортировать данные на этапе прихода данных:

this.Trader.ProcessWellKnownDdeData += (name, dict) => // узнаем, что пришедшие данные отвечают за стакан if (name.Contains("stock")) // первичная сортировка по цене IEnumerable<Quote> _curquotes = (IEnumerable<Quote>)dict; _curquotes = _curquotes.OrderBy(t => t.Price); ... и т.д.

но это ни к чему не привело. Подскажите, пожалуйста, как правильно отсортировать данные?

Еще здесь был как-то уже вопрос про доступ к отдельным значениям котировок в стакане, но объяснения я так и не нашел. Как же все-такии это можно сделать (получить значение той или иной котировки в стакане для анализа)?

Комментарии (82)

Вход или Создать аккаунт, Войдите или зарегистрируйтесь, чтобы оставить комментарий

Mikhail Sukhov
Mikhail Sukhov

Какой пример? Sample?

Насчет Вашей сортировки _curquotes. А дальше вы куда присваиваете _curquotes?

ddd888
ddd888 Автор

Да, это в Sample. Собственно, на этом _curquotes и заканчиваются... Т.е. Вы намекаете на то, что таким образом коллекция, которая затем отображается в окне, осталась прежней? Как же перехватить исходные данные?

Mikhail Sukhov
Mikhail Sukhov

Я думаю это нужно вставить в SecuritiesWindow.

pair.Value.Quotes.AddRange(MainWindow.Instance.Trader.GetMarketDepth(pair.ey).OrderBy(t => t.Price).Select

Манируляции над IEnumerable не изменяют коллекцию, а возвращают новое значение. Собственно оно и содержит необходимые изменения.

ddd888
ddd888 Автор

Точно! Это сработало! Спасибо большое! :)

ddd888
ddd888 Автор

Интересно, а вот почему это работает - не понимаю. :) Ведь окно котировок в момент открытия окна "инструменты" еще не инициализировано. И экспорт "стакана" тоже еще не начал работать... А данная манипуляция с сортировкой происходит в "конструкторе", т.е. в момент создания экземпляра окна "инструменты". Как же это происходит?

Mikhail Sukhov
Mikhail Sukhov

А Вы посмотрите по-внимательнее. Там создается таймер, который из коллекции вытаскивает данные. И посмотрите, в какой момент эта коллекция заполняется.

ddd888
ddd888 Автор

Попытаюсь. Как мне кажется, что CreateTimer() запускается в конструкторе "инструментов" и потом работает постоянно каждую миллисекунду, правильно?

ddd888
ddd888 Автор

Определения CreateTimer() я нигде не нашел. В чем состоит его функция?

Mikhail Sukhov
Mikhail Sukhov

Он находится в Ecng.Common, TimerHelper. Создание таймера по входящему TimeSpan? Можно напрямую создавать через класс Timer, но через TimerHelper запись короче.

ddd888
ddd888 Автор

Добавил исключение в SecuritiesWindow() и обнаружил, что цикл внутри CreateTimer() вызывается как будто лишь при открытии стакана котировок и попытке начала экспорта стакана из квика. В другое время он значит не действует?

Mikhail Sukhov
Mikhail Sukhov

При открытии стакана добавляет элемент в коллекцию. А в таймере идет перечисление по коллекции. Поэтому, при ни одном открытом инструменте, таймер выполняет холостую работу.

ddd888
ddd888 Автор

Спасибо, теперь все гораздо яснее стало. Но при закрытии стакана, насколько я заметил, таймер продолжает работать, да?

Mikhail Sukhov
Mikhail Sukhov

Конечно. Таймер на окно с инструментами. У него может быть несколько стаканов, по одному на инструмент. Если бы он останавливался при закрытие одного стакана, другие бы тоже переставали работать.

ddd888
ddd888 Автор

Т.е. в данном случае таймер выполняет ту же функцию для стакана (импорт данных), что и подписка на события обновления котировок в других окнах (типа "инструменты")?

Mikhail Sukhov
Mikhail Sukhov

Не понял утверждения, если честно.

ddd888
ddd888 Автор

Имел ввиду следующее. Обновление окна "инструменты" - это работа this.Trader.NewSecurities += ..., а обновление окна "стакана" - это работа таймера.

Mikhail Sukhov
Mikhail Sukhov

Верно, только лучше так: а обновление окон "стаканов" - это работа таймера

ddd888
ddd888 Автор

Спасибо. С Вашей помощью туман успешно рассеивается. :)

Сейчас пытаюсь понять структуру данных в загружаемых коллекциях. Не могли бы популярно объяснить, как это работает. Как я понимаю, в конце "инструменты" создается экземпляр коллекции для будущих "стаканов":

private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows = new SynchronizedDictionary<Security, QuotesWindow>();

а при открытии окна котировок в конструкторе создается новый экземпляр но уже другой коллекции:

this.Quotes = new SynchronisedObservableList<SampleQuote>();

SynchronizedDictionary<Security, QuotesWindow> - заполняется котировками, а в чем состоит работа SynchronisedObservableList<SampleQuote>? Визуализация данных? И как они связаны между собой?

Mikhail Sukhov
Mikhail Sukhov

SampleQuote - это тот же Quote, только еще содержащий собственный объем.

SynchronisedObservableList сделал исключительно для визуализации.

Вот тут написано как делать отображение данных в программе -

http://msdn.microsoft.com/ru-ru/library/ms752347.aspx

ddd888
ddd888 Автор

Для своих целей я добавил в SampleQuote дополнительные поля для вывода их в форме. Но никак не получается совладать с "разбором" коллекции. К примеру при формировании отображения в таймере я добавил строку для вывода минимальной цены стакана, чтобы потом ее присвоить к полю SampleQuote:

var _minprice = MainWindow.Instance.Trader.GetMarketDepth(pair.Key).Min(q => q.Price);

Но компилятор ругается, что "последовательность не содержит данных". Хотя в режиме отладки все переменные показывают значения. В чем тут ошибка?

Mikhail Sukhov
Mikhail Sukhov

GetMarketDepth - это стакан MarketDepth.

MarketDepth - это коллекция котировок. Ошибка говорит о том, что коллекция пуста. Такое бывает, когда нет экспорта по стакану (или он еще не стартанул).

ddd888
ddd888 Автор

С коллекцией котировок теперь тоже все понятно. :) Спасибо за полезные ссылки. Прочитал, все что было по отображению данных, но на практике ... никак не могу понять, почему эта конструкция работает:

<ListView ItemsSource="" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler"> <ListView.View> <GridView> <GridViewColumn Width="70" Header="Bid" DisplayMemberBinding="" />

а вот эта (дополнительное текстовое поле в том же окне) нет:

<TextBox Name="AskBox" ... IsReadOnly="True" AcceptsTab="True" Grid.Column="1" Text=""/>

и так тоже нет:

<TextBox Name="BidBox" ... AcceptsTab="True" IsReadOnly="True" MinHeight="0" Grid.Column="1" Text="">

Best Bid - это дополнительное поле в SampleQuote. Причем если Best Bid загрузить в <GridViewColumn>, то данные нормально отображаются, а в текстовом поле нет. Это можно легко объяснить?

ddd888
ddd888 Автор

Похоже, объяснить оказалось нелегко. :) По крайней мере, для меня. Но после долгих "танцев с бубнами" удалось найти решение через обработку событий в коллекции Quotes. Судя по похожим проблемам на многих форумах. по-видимому, это был единственно возможный выход с данной версией WPF.

Mikhail Sukhov
Mikhail Sukhov

Нет, просто был занят релизом.

Text="" не выводится скорее всего потому, что не такого свойства. BestBid - это свойтство MarketDepth. А quotesWindow работаео к коллекцией SampleQuotes.

Text="" - не уверен что WPF понимает такие запросы. Я думаю, проще будет через форумы программерские спрашивать такие вещи. Напримерhttp://rsdn.ru/forum/dotnet.gui/

. Так как я и сам не особо спец в визуальной технологии.

ddd888
ddd888 Автор

Сейчас уже не очень актуально, т.к. решение по привязке уже есть и вроде как работает без сбоев. Как я это понял, проблема не в том, что WPF не понимает запросы - он как раз многое понимает - но не обновляет при обновлении коллекции - в этом я нашел причину и исправив ее через подписку на событие измения коллекции, все заработало.

Возник вопрос другого плана. При отправке своей заявки с помощью создания экземпляра класса NewOrderWindow возникает исключение от "Ecng.Trading.Quik.ApiException: Код ошибки: WrongSyntax Сообщение ACCOUNT=..." и т.д. При отправке из "Инструменты" заявка отправляется нормально, а из моего метода - нет, хотя - я проверил - синтаксис отправляемой заявки точно такой же! Отличие от отправки из окна "Инструменты" лишь в том, что я создаю новый пустой экземпляр Security и приписываю ему код бумаги и класс. В чем тут собака порылась?

Mikhail Sukhov
Mikhail Sukhov

А какой текст транзакции пр которой возникает ошибка?

ddd888
ddd888 Автор

Текст такой (интересно, есть способ как-то его скопировать, чтобы не переписывать? :)):

"ACCOUNT=SPBFUT...;CLIENT_CODE=XXX;TYPE=L;TRANS_ID=3;CLASSCODE=SPBFUT;SECCDE=GZM0;ACTION=NEW_ORDER;OPERATION=B;QUANTITY=10;PRICE=17331;EXECUTION_CONITION=PUT_IN_QUEUE;"

дальше идут:

в [] [] (Int32 [],StringBuilder []) в [] [] (String[], OrderStatus& [], Int32&[], Double& [], Strng& []) и т.д. там где [] - на самом деле квадратики...

ddd888
ddd888 Автор

Поскольку используется одна и та же функция NewOrder, то должно быть дело где-то в составляющих Order order, который параметром в RegisterOrder отправляется? Как же на самом деле формируется класс Order?

ddd888
ddd888 Автор

Пооблему удалось решить, послав в мой класс с методом ссылку на весь экземпляр Security и затем использовав его в формировании заявки. :) Ключевой момент - оказалось, что для формировании заявки только Security.code и Security.class - не достаточно. Что же там еще используется? Т.е. предыдущий вопрос все-таки остается - как формируется класс Order? Какие свойства Security он использует и как?

Mikhail Sukhov
Mikhail Sukhov

А если посмотреть под Debug режимом в студии эти свойства?

ddd888
ddd888 Автор

Да, что хорошо в Ваших уклончивых ответах - они стимулируют процесс познания. :)) Хоролшо бы еще разобраться как это сделать. :) До сих пор это слово - Debug - меня очень сильно пугало своей темной непостижимостью. Наверное, по нему где-то и проходит водораздел между настоящими программистами и любителями. :)

Mikhail Sukhov
Mikhail Sukhov

Ставится точка останова - F9. Запускается программа из студии через F5. Все, работаете в программе до тез пор, пока не сработает точка. Далее, на переменной мышкой и в контекстном меню выбираете нужное действие.

ddd888
ddd888 Автор

Спасибо, оказывается эта техника и называется Debug. :) Все оказалось не так страшно. Я и раньше этим пользовался. Правда, почему-то на точке останова программа не всегда останавливается. Или я что-то не учитываю ...

Есть такой вопрос. Стал переходить на 1.8. Выглядит еще более интересным и навороченным вариантом и поэтому не стал лениться и теперь все новые коды перевожу из 1.7 в 1.8. Вручную, правда, всегда есть риск что-то забыть. Существует ли какой-то алгоритм автоматического внесения изменений?

Mikhail Sukhov
Mikhail Sukhov

Автоматическое внесение изменений - это VS 2008 + руки. Это не шутка. Хотя все не так страшно. Я перевел свои разработки практически за день

  • день тестирование.

Не увидел Вашего поста про ссылка на Security. Можете выделить в простое приложение и прислать если не будет работать. Как вариант, взять SampleConsonle

ddd888
ddd888 Автор

Да, за день я тоже управился с переводом своих кодов и уже добавил кучу новых. :) С ссылкой на Security все в порядке - там все работает. А сейчас плотно работаю с классом Strategy и возникает такая проблема: в моей стратегии на основе Strategy после подачи сигнала к покупке, делаю следующее:

               // создаем заявку
                var order = base.CreateOrder(OrderDirections.Buy,

this.Price, base.Volume); // регистрируем ее (обычным способом лимитированной заявкой) base.Trader.RegisterOrder(order); // добавляем зарегистрированную заявку в стратегию base.AddOrder(order);

т.е. все почти как в Вашем примере. Проверяю затем позицию:

Console.WriteLine(base.PositionManager.Position.ToString() + " | "+ this.PositionManager.Position.ToString());

и в итоге даже после совершения сделки там стоит "0". Почему так происходит? Как правильно отследить совершенную сделку?

Потом почему-то Strategy начинает самопроизвольно делать "отмену ордеров", которых и так нет, что приводит к появлению непрекрающегося потока сообщений об исключении от квика: "нельзя отменить заявку". Что может подавать такие заявки, base.AddOrder(order);?

Если включить сюда trycatch, то такие сообщения исчезают.

Нет ли возможности узнать подробнее "устройство" класса Strategy?

Mikhail Sukhov
Mikhail Sukhov
  1. А точно код base.PositionManager.Position вызывается после появления в программе сделки? если вызывать в NewMyTrades?
  2. Strategy - это абстрактный класс, не имеющий торговой реализации. Он ни регистрирует, ни снимает заявки. Думаю, работает какой-то другой код. try-catch что оборачивали?
ddd888
ddd888 Автор
  1. Код base.PositionManager.Position стоит в методе OnProcess(). Как я понял этот метод исполняется всякий раз с установленным интервалом (у меня 1 сек). Если он исполняется, то ведь и base.PositionManager.Position должен при каждой итерации воспроизводить то значение, которое у него устанавлено данным экземпляром стратегии? Может, я неправильно понимаю смысл слов "появления в программе сделки", но мое понимание, на основании предложенных примеров, такое: а) появление в программе сделки = запуск NewMyTrades, а) после запуска своей стратегии OnStrategyPropertyChanged меняет соответствующие свойства экземпляра стратегии, в т.ч. и свойства PositionManager.Position, которое должно обновляться после совершения сделки, б) _manager.Register(_strategy); запускает менеджер (правда, пока совсем не понимаю, что он конкретно делает, но я старался максимально придерживаться предложенного примера, поэтому оставил его в коде).

  2. try-catch обернул первоначально base.Trader.RegisterOrder(order); и base.AddOrder(order); но сейчас понял, что отмену заявок скорее вызывает этот метод:

base.Orders.Where(o => o.State == OrderStates.Active).ForEach(base.Trader.GuarantyCancelOrder);

но ведь после совершения сделки в рамках стратегии в квике OrderStates.Active должен меняться соответственно? Т.е. если сделка фактически совершилась, то ведь кол-во активных заявок в стратегии должно каждый раз обнуляться? А получается, что это не так - в "активе" остается накопленное кол-во ОТПРАВЛЕННЫХ заявок, а это, как я понимаю, не то, для чего создаваось свойство OrderStates.

Мне кажется нужны более подробные объяснения работы класса, чтобы им можно было безопасно пользоваться. Или, как вариант, может, проще просто привести его код? :)

ddd888
ddd888 Автор

В инстуркции для примера у Вас написано: "Для учета общей прибыли- убытка (P&L) в торговом роботе необходимо использовать дочерние классы от BasePnLManager". В случае со стратегией - все понятно из примера. А как использовать другие дочерние классы? Например, как с помощью SecurityPnLManager рассчитать прибыль-убыток для конкретного инструмента?

Mikhail Sukhov
Mikhail Sukhov

var manager = new SecurityPnLManager(security, trader); manager.PnLChanged += ...

ddd888
ddd888 Автор

Спасибо. Наверное, следовало догадаться. :) В форуме где-то был аналогичный вопрос и как я понял, Ваши классы - менеджеры считают позиции только внутри дня. Т.е. они используют только внтуридневные данные "всех сделок" и "моих сделок"?

Mikhail Sukhov
Mikhail Sukhov

Да.

ddd888
ddd888 Автор

Не получается запустить StrategyPositionManager в окне QuotesWindow. Сделал так:

в поле:

private QuikTrader _trader = MainWindow.Instance.Trader;

в конструкторе:

SecurityPositionManager _secmanager = new SecurityPositionManager(this.Security, _trader); this.SecPos.Content = _secmanager.Position.ToString(); _secmanager.PositionChanged += new Action(_secmanager_PositionChanged);

обработчик:

void _secmanager_PositionChanged() { this.SecPos.Content = _secmanager.Position.ToString();

но при любом раскладе _secmanager.Position.ToString(); показывает 0. StrategyPositionManager при этом работает нормально. Что сделано неправильно?

Mikhail Sukhov
Mikhail Sukhov

this.Sync видели в примере? Я описал зачем это нужно в разделе Пользовательский интерфейс (GUI)

ddd888
ddd888 Автор

Не ругайтесь, я не программист, а только учусь. :) Пример читал, но видимо не совсем понял как работает this.Sync так скать физически. Если цель его - обновление пользовательского окна, то тогда я добавляю его к обработчику и получаю:

void _secmanager_PositionChanged() { this.Sync(() => ); } Делаю тестовый трейд, но позиция по-прежнему - 0... :(

Mikhail Sukhov
Mikhail Sukhov

А событие PositionChanged срабатывает? GetMyTrades(_secmanager.Security) что возвращает (скажем, если вызывать из ITrader.NewMyTrades)?

ddd888
ddd888 Автор

Похоже, что не вызывается почему-то. А GetMyTrades вызывает исключение "в экземпляре объекта не задана ссылка на объект", хотя _secmanager создается при открытии окна.

Mikhail Sukhov
Mikhail Sukhov

Посмотрите ошибку по детальнее. Скорее всего то она и виновата в причине не обновления позы.

ddd888
ddd888 Автор

Смотрю. При остановке программы в месте подписки на событие экземпляр _secmanager = null. Как это может быть, не те аргументы при создании SecurityPositionManager? Может, не тот шлюз? У меня шлюз тот же, что и в начале программы Sample, т.е. MainWindow.Instance.Trader.

ddd888
ddd888 Автор

Заметил, что в топике по аналогичному вопросу http://groups.google.ru/

group/stocksharp/browse_thread/thread/7334ab4cd0ed9bd9) уже задавлся этот вопрос. Но Ваш ответ там касался добавления заявки в стретагию (Strategy.AddOrder.), а вопрос был про SecurityPnLManager. Тогда такой вопрос - чем работа SecurityPnLManager отличается от StrategyPnLManager? Как я понимаю в случае с SecurityPositionManager и StrategyPositionManager ситуация аналогичная?

ddd888
ddd888 Автор

Все перепроверил еще раз: при создании экземпляра SecurityPositionManager его свойство Security остается = null, хотя в параметрах идет явная ссылка на экземпляр типа Security. Может, баг в конструкторе SecurityPositionManager?

Mikhail Sukhov
Mikhail Sukhov

У Вас другое - ошибка в обработчике. Какой у Вас лог ошибки?

ddd888
ddd888 Автор

Так обработчик не вызывается. События PositionChanged не происходит. а если использовать свойства экземпляра, то грит, что ссылка на пустой объект. secmanager.Security = null в любом месте кода.

ddd888
ddd888 Автор

При вызове _trader.MyTrades.Where(d => d.Order.State == OrderStates.Matched).Select(d => d) все заявки оказываются == Matched хотя в окне квика реально есть активные.

а

this.Secmanager = new SecurityPositionManager (q,_trader ); Secmanager.PositionChanged += new Action(_secmanager_PositionChanged);

реально обновляется только после работы стратегии, т.е. вместе со StrategyPositionManager. Хотя насколько я понимаю предполагалось, что он должен показывать реальную позицию по инструменту независимо от времени включения программы или работы стратегии?

GetMyTrades при этом выводит все сделки по инструменту нормально.

Mikhail Sukhov
Mikhail Sukhov

Это потому что Вы в него передаете null в качестве инструмента. Посмотрите в коде, почему так получается.

А как иначе, если у Вас написано "дайте все мои сделки, у которых состояние Matched и сконвертировать из MyTrade в MyTrade (что уж совсем бессмысленно)".

Правильно, но рассчитывает позицию он по таблице Мои Сделки. Там записи есть?

ddd888
ddd888 Автор

Так это читается "сконвертировать"! :) Везде в справочниках написано "проецировать" и оказывается я не так истолковал суть метода. Спасибо за очередную подсказку.

С null разобрался. Почему-то при передаче экземпляра класса Security через индексатор (get,set) он не сразу "активизируется" и при инициализации SecurityManager'а, соответственно, Security в его параметрах = null. Лишь передав параметр Security через конструктор SecurityManager получил "ненулевой" инструмент. Но почему оно так работает - пока не понял.

Да, в таблице "Мои сделки" есть записи. И сегодня при включении SecurityManager вроде начал давать правильную информацию. :) Но наблюдать пока продолжаю. :)

ddd888
ddd888 Автор

А при новом запуске программы SecurityManager.Position снова показывает 0.. В моих сделках запись при этом есть. SecurityManager нормально активизирован, инструмент "в нем" есть. SecurityManager.Trades = 1. Это правильно. А Position почему-то 0...

Mikhail Sukhov
Mikhail Sukhov

Это не индексатор, а свойство. И через него передать нельзя - оно private.

Версия 1.8?

ddd888
ddd888 Автор

Даже если у меня свойство было объявлено public?

Да, 1.8

Mikhail Sukhov
Mikhail Sukhov

У меня работает. Попробуйте еще вызвать метод Init

ddd888
ddd888 Автор

Да, с init функция заработала нормально. Спасибо.

Не получается добавить комментарий в заявку. Делаю так:

((QuikTrader)_trader).FormatTransaction += (str) => {str.SetComment(_currentoperationstring);}; base.Trader.RegisterOrder(order);

Но в квике все те же "ХХХ". Пробовал так:

order.Comment = _currentoperation; base.Trader.RegisterOrder(order);

Реультат тот же. Почему так получается?

ddd888
ddd888 Автор

Т.е., чтобы изменить строку транзакции сначала надо убрать соотвествующую инструкцию, а потом заново ее написать? Типа

  1. ((QuikTrader)_trader).FormatTransaction += builder => builder.RemoveInstruction(TransactionBuilder.Comment);
  2. ((QuikTrader)_trader).FormatTransaction += str => str.SetComment(_mycomment); ?
ddd888
ddd888 Автор

Да, все работает, только вместо TransactionBuilder.Comment почему-то надо использовать TransactionBuilder.ClientCode тогда "Комментарий" изменяется. А почему не свойство Comment отвечает за комментарий - ведь это логичнее?

Mikhail Sukhov
Mikhail Sukhov

Особенности настроек Квик сервера.

ddd888
ddd888 Автор

В связи с одним обсуждением (о задержке вывода стакана) возник вопрос: с какой целью в окне "инструменты" был создан таймер, если экспорт стакана можно просто запустить через методы RegisterQuotes и GetMarketDepth?

Кстати, GetMarketDepth.BestBid и GetMarketDepth.BestAsk показывают не лучший бид и аск соответственно, а вообще максимальную и минимальную цены стакана. По-моему, так не должно быть.

Mikhail Sukhov
Mikhail Sukhov
  1. RegisterQuotes запускает ДДЕ экспорт для стакана.
  2. Получается этот стакан GetMarketDepth
  3. А кто будет в цикле это вызывать? Цикл нужен потому что стакан не статичен и меняется каждый раз. В отличие от таблицы с инструментами, в стакане Quote пересоздается каждый раз, а вот объект Security живет всю жизнь. Что и логично - инструмент о новый каждый раз, а заявки снимаются и исполняются.

GetMarketDepth.BestBid и GetMarketDepth.BestAsk - а как Вы это видите?

ddd888
ddd888 Автор

GetMarketDepth.BestBid и GetMarketDepth.BestAsk получаю просто через GetMarketDepth(this.Security).BestBid.Price. В консоле эти значения соответствеено показывают для данного стакана минимальную и максимальную котировки соответственно.

Я начал тут "копать" по причине того, что уже много "наворотил" в своем GUI для управления стаканом. (Пока S#1.8) И недавно обнаружил, что при переходе на наиболее волатильные фьючи, типа RTS (до этого я тестировал все на сравнительно маловолатильных фьючерсах), стакан начинает резко тормозить. До запуска стакана вывод нормальный, а при запуске - начинается тормоз. Как будто что-то не успевает обрабатываться. Начал выводить данные в консоль - ситуация выглядит получше, но все равно - запаздывает на 5-10 секунд, а то и больше. Вот и ищу причину.

Про цикл не совсем понял. Если можно подписаться на событие GetMarketDepth.Changed и затем получить новые котировки, то зачем нужен цикл? (Или наоборот - если нужен цикл, то зачем нужно событие GetMarketDepth.Changed?) Наверное я не совсем правильно понимаю механику работы GetMarketDepth...

Mikhail Sukhov
Mikhail Sukhov

На GetMarketDepth.Changed можно подписаться. Но если открыть несколько стаканов - то ГУИ это убъет. GetMarketDepth.Changed вызывается очень часто. И в каждом из этих вызовов необходимо делать BeginInvoke (или Sync). А вот таймер всегда стабильно раз в несколько секунд, и не зависит от количество открытых стаканов.

ddd888
ddd888 Автор

Да, я уже понял, что ресурсов компьютера хватит для более-менее эффективной обработки лишь одного стакана. Наверное, надо переносить большую часть обработки "внутрь" процессов, а на ГУИ выводить лишь самое необходимое. Придется теперь подробнее заниматься производительностью кода. Или искать альтернативы DDE? Хотя, интересно, вот в самом же квике десятки тысяч данных обновляются без видимых затруднений - значит ли это, что у них технология лучше?

ddd888
ddd888 Автор

Столкнулся с таким сообщением от квика: "Вывод в wrapper по DDE приостановлен. Переполнена очередь сообщений." Что это значит?

Mikhail Sukhov
Mikhail Sukhov

Не обработки, а отрисовки. Обработка делается моментально. И как плюс, отказываться от сложного ГУИ. Я все по логам делаю.

Mikhail Sukhov
Mikhail Sukhov

По все видимости, принимающая сторона (робот) умерла.

ddd888
ddd888 Автор

Может, это отрисовка WPF оставляет желать лучшего? Windows.Forms не быстрее будет? Интересно, есть какие-нибудь исследования/рейтинги на этот счет?

Mikhail Sukhov
Mikhail Sukhov

Это лучше узнавать на программерских форумах - они то уж точно знают.

ddd888
ddd888 Автор

Интересно, а есть ли способ как-то протестировать время поступления какой-нибудь единицы данных? Скажем - от начала экспорта из квика до получения их на каком-нибудь выходе в ГУИ?

Mikhail Sukhov
Mikhail Sukhov

Я думаю только визуально. Писать в консоль время поступления данных, и смотреть при этом на ГУИ.

ddd888
ddd888 Автор

Сегодня на одном из этапов тестирования, когда котировки в очередной раз стали тормозить, случайно столкнулся с таким феноменом: квик в какой-то момент внезапно "вылетел", т.е. закрылся совсем, а в мою программу при этом продолжали поступать данные! Т.е. ощущение, что в DDE образовалась какая-то очередь, которую программа постепенно "пережевывала". Такое может быть? Т.е. эта очередь данных может реально где-то в памяти компьютера находится, пока не будет "потреблена"?

Mikhail Sukhov
Mikhail Sukhov

Да, очередь присутствует. И сколько по времени еще так жил экспорт?

ddd888
ddd888 Автор

Довольно долго. Я несколько минут ждал, но окончания так и не дождался...

Mikhail Sukhov
Mikhail Sukhov

Значит Квик не вылетел. Там максимум (при очень медленном компьютере) будет пару секунд.

ddd888
ddd888 Автор

Вылетел стопудово. И сообщение появилось о какой-то ошибке. Повторный запуск прошел нормально. А если квик в системе, то второй раз его не запустишь - появится соответствующее сообщение. Это случилось на фоне тормоза котировок стакана по волатеильному инструменту после запуска экспорта всех сделок. Я и сейчас отслеживаю котировки в консоли - они начинают сильно не совпадать с квиковскими имено после таблицы запуска всех сделок. И даже трудно понять, насколько сильно запаздывание, потому что котировки и там, и там весьма разные - визуально увидеть "хвост" не получается. Без "всех сделок" - все идет нормально.

Mikhail Sukhov
Mikhail Sukhov

Когда запускается Все сделки, то тормозить и должно. До минуты. Потому что данных много, и необходимо время на обработку. А вот после - должно работать как обычно.