Комментарии и предложения
Atom Ответить
11.03.2011


Заметил пару нюансов по PlazaTrader:
1)
Code

_plazaStreamManager = new PlazaStreamManager(_plazaConnectionPool, TRequestType.RT_COMBINED_DYNAMIC, 1000);

Почему рекваест тайп фиксированный? надо конкретно задавать, например у меня так было:
Quote:

"FORTS_FUTAGGR20_REPL" - RT_REMOTE_ONLINE
"FORTS_FUTTRADE_REPL" - RT_REMOTE_ONLINE
"FORTS_FUTINFO_REPL" - RT_COMBINED_DYNAMIC
"FORTS_FUTCOMMON_REPL" - RT_REMOTE_ONLINE
"FORTS_POS_REPL" - RT_COMBINED_DYNAMIC
"FORTS_PART_REPL" - RT_COMBINED_DYNAMIC
"FORTS_VM_REPL" - RT_COMBINED_DYNAMIC

Допустим если FORTS_FUTCOMMON_REPL задать в RT_COMBINED_DYNAMIC, то при реконнекте будет перекачиваться весь поток заново.
+Revision тоже надо указывать в некоторых потоках.
2) в PlazaStreamManager void Run()
Не обрабатывает состояния типа DS_STATE_ERROR или DS_STATE_CLOSE, те в случае чего будет затык и поток будет висеть закрытым или с ошибкой.
Я для себя брал реализацию с примера(могу выложить если что)
PS У меня несколько сместились приоритеты - придется задержаться на квике, но постораюсь не пропадать.

Теги:


Спасибо:




60 Ответов
< 1 2 3  >
aspirant

Фотография
Дата: 26.03.2011
Ответить


Mikhail Sukhov Go to
Сейчас PlazaListener создается в самом начале со всеми колонками. Как я уже писал, использовать надо только те колонки, что определены схемами PlazaTable.Columns.

Как клиентский код создает таблицу с колонками и схемами? Твое видение с куском кода.

Mikhail Sukhov Go to
enum TableName лучше сделать статическим классом со строковыми константами. Я так понял, такие безобразные названия нужны лишь для того, чтобы ini файлы создавать? Если да, то по логике простой. Мы создаем текстовый файл, используем string. Вся логика в имени полей TableName - приведение в string... Так может тогда сразу сделать string?

Этот enum был служебным, для внутреннего пользования. Клиенту его можно не показывать. Зачем я его сделал? Когда нужно набить пару десятков классов, enum + intellisense здорово помогает. Потом подстраховка от случайное опечатки в string. Сам я его не набивал: вместо этого пропарсил папку со всеми схемами.

Вообще PlazaTable сейчас и есть то, что ты предлагаешь. За исключением полей метаданных все остальные методы закомментены (причем, делал это не я). Можно сделать вот так:
Code
// вместо: public TableName SystemName { get; private set; }
public string SystemName { get{ _systemName.ToString(); }


Mikhail Sukhov Go to
Давайте подумаем насчет фильтрации. Сейчас это выглядит как то не очень.

Я вообще собирался это делать внутри PlazaListener, но пока думал, skuvv успел привинтить свой фильтрSmile. Что насчет методов RegisterSecurity/UnRegisterSecurity? С помощью них будем включать фильтрацию или они нужно для чего-то другого?

Mikhail Sukhov Go to
PlazaSchemaParser предлагаю упрятать в PlazaTableSerializer

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

skuvv

Фотография
Дата: 26.03.2011
Ответить


Mikhail Sukhov Go to
skuvv Go to
aspirant Go to
skuvv Go to

К примеру я сравнил проект со своей старой программой plaza2, данные в текущий s# проект поступают медленнее на 6-10мс(время снимал с OnNewSecurityChanged к примеру).


Получается, ты делал свою реализацию PlazaTrader?

Нет у меня не интерфейс как в s#, а напрямую работа c потоками и событиями


Может выложить это ввиде отдельного теста для сравнения перфоманса? Насчет OnNewSecurityChanged - конечно. Если вместо поиска по словарю устраивать Exist Find и т.д., то я не удивлюсь и пол минутными задержками.Smile

Code

void m_conn4_StreamDataInserted(CP2DataStream stream, String tableName, CP2Record rec)
{
try
{
if (stream.StreamName == streamFutCommonID)
{
int index = instruments.GetIndex(rec.GetValAsLong("isin_id"));
if(index>=0)
{
double best_sell = Convert.ToDouble(rec.GetValAsVariant("best_sell"));
double best_buy = Convert.ToDouble(rec.GetValAsVariant("best_buy"));
if (best_buy != 0 && best_sell != 0)
{
instruments._bestask[index] = best_sell;
instruments._bestask_vol[index] = rec.GetValAsLong("amount_sell");
instruments._bestbid[index] = best_buy;
instruments._bestbid_vol[index] = rec.GetValAsLong("amount_buy");
instruments._lasttrade[index] = Convert.ToDouble(rec.GetValAsVariant("price"));
instruments._lasttrade_vol[index] = rec.GetValAsLong("amount");
instruments._lasttrade_time[index] = Convert.ToDateTime(rec.GetValAsString("deal_time"));
instruments._bidask_time[index] = DateTime.Now;

curr_rev_futcommon = rec.GetValAsLong("replRev");

log_buffer.Add(t4.Name, rec.GetValAsString("replID") + ";" + rec.GetValAsString("replRev") + ";" + instruments._isin_id[index] + ";" + instruments._bestask[index] + ";" + instruments._bestask_vol[index] + ";" +
instruments._bestbid[index] + ";" + instruments._bestbid_vol[index] + ";" + instruments._lasttrade[index] + ";" + instruments._lasttrade_vol[index] + ";" + instruments._lasttrade_time[index].ToString(timefmt) + ";" + rec.GetValAsString("deal_count") + ";" + rec.GetValAsString("pos"));
}
}
}
}
catch (Exception e)
{
log_buffer.Add("error", "[" + t4.Name + "] " + e.ToString());
}
}

Event datainserted
Все типы таблицы я получал в соответсвующих им win thread.
Событий никаких нет BigGrin, заполняется статик массив instruments,а роботы на каждом прогоне его чекают.
Ну и в конце запись в текстовики, через самописный логгер.
ps фильтрация опять же массиву instruments
Автор топика
Спасибо:

Mikhail Sukhov

Фотография
Дата: 27.03.2011
Ответить


aspirant Go to
Mikhail Sukhov Go to
Сейчас PlazaListener создается в самом начале со всеми колонками. Как я уже писал, использовать надо только те колонки, что определены схемами PlazaTable.Columns.

Как клиентский код создает таблицу с колонками и схемами? Твое видение с куском кода.


Взял за основу отсюда:

Code
PlazaTableRegistry.FutCommon.Columns.Add(PlazaColumnRegistry.FuturesCommon.CurKotir);

PlazaTableRegistry.FutAggr5.Columns.Add(PlazaColumnRegistry.FuturesAggregation5.Moment);


aspirant Go to

Mikhail Sukhov Go to
enum TableName лучше сделать статическим классом со строковыми константами. Я так понял, такие безобразные названия нужны лишь для того, чтобы ini файлы создавать? Если да, то по логике простой. Мы создаем текстовый файл, используем string. Вся логика в имени полей TableName - приведение в string... Так может тогда сразу сделать string?

Этот enum был служебным, для внутреннего пользования. Клиенту его можно не показывать. Зачем я его сделал? Когда нужно набить пару десятков классов, enum + intellisense здорово помогает. Потом подстраховка от случайное опечатки в string. Сам я его не набивал: вместо этого пропарсил папку со всеми схемами.


Ок, не поняли друг друга. Тогда ремарка и код. Если enum используется для задания строковых констант, то используется класс со строками:

Code
static class TableName
{
public const string BaseContractsParams = "base_contracts_params";
..
}

public static PlazaTable FutAggr50 = new PlazaTable(TableName.BaseContractsParams, ReplicationStream.FutAggr50, "Фьючерсы: стакан глубиной 50 котировок");


На этом примере сразу видно недостаток именования перечислений (не ReplicationStreams, а ReplicationStream). Сходу кажется, что идет работа с потоком. А на самом деле к потокам это имеет о посредственное значение.

aspirant Go to

Вообще PlazaTable сейчас и есть то, что ты предлагаешь. За исключением полей метаданных все остальные методы закомментены (причем, делал это не я). Можно сделать вот так:
Code
// вместо: public TableName SystemName { get; private set; }
public string SystemName { get{ _systemName.ToString(); }



Не понял, что за рокировка.

aspirant Go to

Mikhail Sukhov Go to
Давайте подумаем насчет фильтрации. Сейчас это выглядит как то не очень.

Я вообще собирался это делать внутри PlazaListener, но пока думал, skuvv успел привинтить свой фильтрSmile. Что насчет методов RegisterSecurity/UnRegisterSecurity? С помощью них будем включать фильтрацию или они нужно для чего-то другого?


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

aspirant

Фотография
Дата: 28.03.2011
Ответить


Mikhail Sukhov Go to
Code
PlazaTableRegistry.FutCommon.Columns.Add(PlazaColumnRegistry.FuturesCommon.CurKotir);

PlazaTableRegistry.FutAggr5.Columns.Add(PlazaColumnRegistry.FuturesAggregation5.Moment);


Вопросы:
  1. То есть мы исходим из того, что клиент подписывается на каждый поток только один раз? Напоминаю, у Плазы можно подписываться на один поток несколько раз. Клиенты не будут спрашивать? Когда проект будет зарелизен, изменить архитекутру будет сложно.
  2. Что делать с нашими стандартными потоками, которые нужны, чтобы мапить объекты. Мы договорились, что их скроем от клиента, чтобы гарантировать в них нужный набор колоннок. Придется все равно создавать какие-то отдельные таблицы.


Mikhail Sukhov Go to
Если enum используется для задания строковых констант, то используется класс со строками:
Code
static class TableName...


OK, сделаю после фильтрации. Сейчас все работает, а это рефакторинг функционала не добавит, только эстетику.

Mikhail Sukhov Go to
Не понял, что за рокировка.

Теперь она уже не нужна.

Mikhail Sukhov Go to
На этом примере сразу видно недостаток именования перечислений (не ReplicationStreams, а ReplicationStream)


Не знаю, дело вкуса. Я уже привык к единственному числу. Вот, кстати, MSDN'овский guideline.
Спасибо:

Mikhail Sukhov

Фотография
Дата: 28.03.2011
Ответить


aspirant Go to

Вопросы:
  1. То есть мы исходим из того, что клиент подписывается на каждый поток только один раз? Напоминаю, у Плазы можно подписываться на один поток несколько раз. Клиенты не будут спрашивать? Когда проект будет зарелизен, изменить архитекутру будет сложно.
  2. Что делать с нашими стандартными потоками, которые нужны, чтобы мапить объекты. Мы договорились, что их скроем от клиента, чтобы гарантировать в них нужный набор колоннок. Придется все равно создавать какие-то отдельные таблицы.



1. Давай еще раз обсудим. С точки зрения практики - зачем это нужно?
2. Хм, а почему бы просто не оставить как есть сейчас?
Спасибо:

aspirant

Фотография
Дата: 29.03.2011
Ответить


Mikhail Sukhov Go to
1. Давай еще раз обсудим. С точки зрения практики - зачем это нужно?


Сегодня думал на эту тему. Пожалуй, действительно ты прав: нужно упростить систему. Тем более что фильтры будут носить глобальный характер, т.е. вызвал RegisterSecurity(Security security) для какой-то бумаги, и все потоки сразу стали фильтровать только по ней, добавил еще одну - все потоки фильтруются по этим двум бумагам. Я правильно понял? Вопрос: события, связанные с приходом данных (DataInserted, DataDeleted и т.д.), перекидываю из PlazaListener в PlazaTable? Или всё в PlazaTrader, и добавляю в события первым параметром PlazaTable, чтобы можно было различать, из какой таблицы пришли данные? Больше нравится первое.

Mikhail Sukhov Go to
2. Хм, а почему бы просто не оставить как есть сейчас?

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

Mikhail Sukhov

Фотография
Дата: 29.03.2011
Ответить


aspirant Go to

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


Можно и не убирать. Мне вообще нравиться вот такой вариант юзер кода:

Code
var trader = new PlazaTrader();

trader.UsdOnlineListener.DataInserted += record =>
{
....
};

trader.StartListener(trader.UsdOnlineListener);

...

trader.StopListener(trader.UsdOnlineListener);


И для стандартных таблиц можно так же делать:

Code
public override StartExport()
{
StartListener(VcbFutureListener, CommonFutureListener, CommonOptionListener,...);
}
Спасибо:

aspirant

Фотография
Дата: 30.03.2011
Ответить


Mikhail Sukhov Go to

Можно и не убирать. Мне вообще нравиться вот такой вариант юзер кода:

Code
var trader = new PlazaTrader();

trader.UsdOnlineListener.DataInserted += record =>
{
....
};

trader.StartListener(trader.UsdOnlineListener);

...

trader.StopListener(trader.UsdOnlineListener);



То есть получается пользователь не сможет создавать объекты типа PlazaListener? И каждому классу-наследнику PlazaColumns (PlazaAggregationColumns и т.д.) внутри класса PlazaTrader будет соответствовать открытая переменная типа PlazaListener?

Quote:
Тем более что фильтры будут носить глобальный характер, т.е. вызвал RegisterSecurity(Security security) для какой-то бумаги, и все потоки сразу стали фильтровать только по ней, добавил еще одну - все потоки фильтруются по этим двум бумагам.


Я правильно понял?
Спасибо:

Mikhail Sukhov

Фотография
Дата: 30.03.2011
Ответить


aspirant Go to
То есть получается пользователь не сможет создавать объекты типа PlazaListener? И каждому классу-наследнику PlazaColumns (PlazaAggregationColumns и т.д.) внутри класса PlazaTrader будет соответствовать открытая переменная типа PlazaListener?


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

Каждому члену PlazaTableRegistry.

aspirant Go to

Quote:
Тем более что фильтры будут носить глобальный характер, т.е. вызвал RegisterSecurity(Security security) для какой-то бумаги, и все потоки сразу стали фильтровать только по ней, добавил еще одну - все потоки фильтруются по этим двум бумагам.


Я правильно понял?


RegisterSecurity фильтрует только те потоки, которые относяться к инструментам. На потоки со сделками или стаканами он никак не влияет. То же самое справедливо и для других RegisterXXX
Спасибо:

Mikhail Sukhov

Фотография
Дата: 30.03.2011
Ответить


Mikhail Sukhov Go to
RegisterSecurity фильтрует только те потоки, которые относяться к инструментам. На потоки со сделками или стаканами он никак не влияет. То же самое справедливо и для других RegisterXXX


И сразу код, как бы это можно было фильтровать:

Code
var usdOnlineListener = trader.UsdOnlineListener;
usdOnlineListener.AddFilter(rih1);
...
usdOnlineListener.RemoveFilter(rih1);


Соответственно, реализация методов ITrader.RegisterXXX так же вызывает внутри себя AddFilter.
Спасибо:

aspirant

Фотография
Дата: 30.03.2011
Ответить


Mikhail Sukhov Go to

И сразу код, как бы это можно было фильтровать:

Code
var usdOnlineListener = trader.UsdOnlineListener;
usdOnlineListener.AddFilter(rih1);
...
usdOnlineListener.RemoveFilter(rih1);



Залил. Посмотри.
Спасибо:

Mikhail Sukhov

Фотография
Дата: 31.03.2011
Ответить


aspirant Go to

Залил. Посмотри.


Отредактировал. Кое что поменял. Например, идентификатор Плазы лучше засунуть по внутрь. Чтобы можно было без проблем его менять. Сейчас идентификатор составной из кода и класса для единообразия с остальными ITrader.
Спасибо:

aspirant

Фотография
Дата: 31.03.2011
Ответить


Mikhail Sukhov Go to
Отредактировал. Кое что поменял.

Все понятно.

Mikhail Sukhov Go to
aspirant Go to
То есть получается пользователь не сможет создавать объекты типа PlazaListener? И каждому классу-наследнику PlazaColumns (PlazaAggregationColumns и т.д.) внутри класса PlazaTrader будет соответствовать открытая переменная типа PlazaListener?


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

Каждому члену PlazaTableRegistry.


Вопрос: если мы оставляем конструктор публичным, и пользователь сможет сам создавать объекты типа PlazaListener, зачем тогда создавать открытые переменные для каждого члена PlazaTableRegistry? Не будет ди это дублированием схожего функционала? Либо пользователь сам подписывается на потоки через свои объекты PlazaListener, либо мы выдаем ему строгий набор переменных и они получает информацию из потоков через наши открытые переменные?
Спасибо:

Mikhail Sukhov

Фотография
Дата: 01.04.2011
Ответить


aspirant Go to

Вопрос: если мы оставляем конструктор публичным, и пользователь сможет сам создавать объекты типа PlazaListener, зачем тогда создавать открытые переменные для каждого члена PlazaTableRegistry? Не будет ди это дублированием схожего функционала? Либо пользователь сам подписывается на потоки через свои объекты PlazaListener, либо мы выдаем ему строгий набор переменных и они получает информацию из потоков через наши открытые переменные?


Мне нравиться второй вариант.
Спасибо:

skuvv

Фотография
Дата: 01.04.2011
Ответить


Сделайте плиз OnNewDataFromPosition, у меня не получается Confused
Автор топика
Спасибо:

aspirant

Фотография
Дата: 01.04.2011
Ответить


Mikhail Sukhov Go to
Отредактировал. Кое что поменял. Например, идентификатор Плазы лучше засунуть по внутрь. Чтобы можно было без проблем его менять. Сейчас идентификатор составной из кода и класса для единообразия с остальными ITrader.


В методе OnNewDataFromPosition нужно находить уже существующую бумагу, а известен только IsinId. Создавать еще один Dictionary, или все таки не использовать составной идентификатор. Кстати, такая же проблема будет встречаться в других потоках.
Спасибо:

aspirant

Фотография
Дата: 01.04.2011
Ответить


skuvv Go to
Сделайте плиз OnNewDataFromPosition, у меня не получается Confused


Пока сделал заглушку, чтобы код компилился. Есть вопрос к Михаилу + не знаю, что делать внутри лямбды Func<Portfolio, Security, Position> createPosition.
Спасибо:

skuvv

Фотография
Дата: 02.04.2011
Ответить


aspirant Go to
Mikhail Sukhov Go to
Отредактировал. Кое что поменял. Например, идентификатор Плазы лучше засунуть по внутрь. Чтобы можно было без проблем его менять. Сейчас идентификатор составной из кода и класса для единообразия с остальными ITrader.


В методе OnNewDataFromPosition нужно находить уже существующую бумагу, а известен только IsinId. Создавать еще один Dictionary, или все таки не использовать составной идентификатор. Кстати, такая же проблема будет встречаться в других потоках.

Тут однозначно надо вначале формировать список инструментов, а потом плясать от него, те общий start делать внутри метода по этапам, например дождаться OnStreamDataEnd от инструментов и запустить остальные потоки данных
Автор топика
Спасибо:

Mikhail Sukhov

Фотография
Дата: 02.04.2011
Ответить


skuvv Go to

Тут однозначно надо вначале формировать список инструментов, а потом плясать от него, те общий start делать внутри метода по этапам, например дождаться OnStreamDataEnd от инструментов и запустить остальные потоки данных


Сначала опишу проблему с идентификаторами Плазы. Если сейчас взять за основу IsinId и записывать его в Security.Id, то для таких инструментов не будет работать ни Гидра, ни тестирование на истории, так как S# использует единую систему именования Code@Class. Поэтому, мне кажется, что будет правильный вариант skuvv дождаться информации об инструменте, и только затем формировать производную информацию.
Спасибо:

aspirant

Фотография
Дата: 02.04.2011
Ответить


Mikhail Sukhov Go to
Мне нравится второй вариант.


Тогда конструктор PlazaListener должен быть internal. Правлю?
Спасибо:

aspirant

Фотография
Дата: 02.04.2011
Ответить


Mikhail Sukhov Go to
skuvv Go to

Тут однозначно надо вначале формировать список инструментов, а потом плясать от него, те общий start делать внутри метода по этапам, например дождаться OnStreamDataEnd от инструментов и запустить остальные потоки данных


S# использует единую систему именования Code@Class. Поэтому, мне кажется, что будет правильный вариант skuvv дождаться информации об инструменте, и только затем формировать производную информацию.


Насчет поэтапности - сделаем. Вопрос остается с IsinId. Посмотри еще раз реализацию OnNewDataFromPosition. Из потока приходит запись с IsinId бумаги. Ее класс (opt или fut) я не знаю. Именно поэтому я написал:
Code
base.GetSecurity(CreateSecurityId(plazaId.ToString(), "InvalidClass")), // TODO Поменять


Я могу найти бумагу, например, так:
Code
var sec = this.Securities.Where(x => x.ExtensionInfo["IsinId"].ToString() == "123456");


Но эту будет перебор списка, а не поиск по словарю. Или можно сделать по-другому?

Спасибо:

aspirant

Фотография
Дата: 02.04.2011
Ответить


Mikhail Sukhov Go to
И каждому члену PlazaTableRegistry внутри класса PlazaTrader будет соответствовать открытая переменная типа PlazaListener


Добавил все переменные. Пока создаются со всеми колонками в методе CreateUserListeners().

Теперь: ты предлагаешь, чтобы при запуске PlazaTrader парсил файлы-схемы в заданной папке и создавал переменные PlazaListener с наборами колоннок, заданными пользователем. Вопрос: в каком месте пользователь будет задавать эти колоннки?
Спасибо:

Mikhail Sukhov

Фотография
Дата: 02.04.2011
Ответить


aspirant Go to
Теперь: ты предлагаешь, чтобы при запуске PlazaTrader парсил файлы-схемы в заданной папке и создавал переменные PlazaListener с наборами колоннок, заданными пользователем.


Не PlazaListener, а PlazaTable, который передается в PlazaListener.

aspirant Go to
Вопрос: в каком месте пользователь будет задавать эти колоннки?


У себя в коде. До Connect, я так понимаю (после подключение ведь нельзя уже править метаданные?)
Спасибо:

Mikhail Sukhov

Фотография
Дата: 02.04.2011
Ответить


aspirant Go to

Но эту будет перебор списка, а не поиск по словарю. Или можно сделать по-другому?


Вопрос уже содержит ответ.Smile
Спасибо:

aspirant

Фотография
Дата: 02.04.2011
Ответить


Mikhail Sukhov Go to
skuvv Go to

Тут однозначно надо вначале формировать список инструментов, а потом плясать от него, те общий start делать внутри метода по этапам, например дождаться OnStreamDataEnd от инструментов и запустить остальные потоки данных


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


Залил исправление. Кстати, дожидаться, похоже, нужно не OnStreamDataEnd, а OnPlazaStreamStateChanged, где state == TDataStreamState.DS_STATE_ONLINE. Может быть это из-за того, что сегодня выходной, но по потокам FORTS_FUTCOMMON_REPL\common и FORTS_OPTCOMMON_REPL\common, OnStreamDataEnd я так и не дождался.
Спасибо:
< 1 2 3  >

Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy