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


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

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

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

"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 Перейти
Сейчас PlazaListener создается в самом начале со всеми колонками. Как я уже писал, использовать надо только те колонки, что определены схемами PlazaTable.Columns.

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

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

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

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


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

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

Mikhail Sukhov Перейти
PlazaSchemaParser предлагаю упрятать в PlazaTableSerializer

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

skuvv

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


Mikhail Sukhov Перейти
skuvv Перейти
aspirant Перейти
skuvv Перейти

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


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

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


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

Код

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 Перейти
Mikhail Sukhov Перейти
Сейчас PlazaListener создается в самом начале со всеми колонками. Как я уже писал, использовать надо только те колонки, что определены схемами PlazaTable.Columns.

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


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

Код
PlazaTableRegistry.FutCommon.Columns.Add(PlazaColumnRegistry.FuturesCommon.CurKotir);

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


aspirant Перейти

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

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


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

Код
static class TableName
{
public const string BaseContractsParams = "base_contracts_params";
..
}

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


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

aspirant Перейти

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



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

aspirant Перейти

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

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


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

aspirant

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


Mikhail Sukhov Перейти
Код
PlazaTableRegistry.FutCommon.Columns.Add(PlazaColumnRegistry.FuturesCommon.CurKotir);

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


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


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


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

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

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

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


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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 28.03.2011
Ответить


aspirant Перейти

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



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

aspirant

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


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


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

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

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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 29.03.2011
Ответить


aspirant Перейти

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


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

Код
var trader = new PlazaTrader();

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

trader.StartListener(trader.UsdOnlineListener);

...

trader.StopListener(trader.UsdOnlineListener);


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

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

aspirant

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


Mikhail Sukhov Перейти

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

Код
var trader = new PlazaTrader();

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

trader.StartListener(trader.UsdOnlineListener);

...

trader.StopListener(trader.UsdOnlineListener);



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

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


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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 30.03.2011
Ответить


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


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

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

aspirant Перейти

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


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


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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 30.03.2011
Ответить


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


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

Код
var usdOnlineListener = trader.UsdOnlineListener;
usdOnlineListener.AddFilter(rih1);
...
usdOnlineListener.RemoveFilter(rih1);


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

aspirant

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


Mikhail Sukhov Перейти

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

Код
var usdOnlineListener = trader.UsdOnlineListener;
usdOnlineListener.AddFilter(rih1);
...
usdOnlineListener.RemoveFilter(rih1);



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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 30.03.2011
Ответить


aspirant Перейти

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


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

aspirant

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


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

Все понятно.

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


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

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


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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 31.03.2011
Ответить


aspirant Перейти

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


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

skuvv

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


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

aspirant

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


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


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

aspirant

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


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


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

skuvv

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


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


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

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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 02.04.2011
Ответить


skuvv Перейти

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


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

aspirant

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


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


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

aspirant

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


Mikhail Sukhov Перейти
skuvv Перейти

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


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


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


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


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

Спасибо:

aspirant

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


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


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

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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 02.04.2011
Ответить


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


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

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


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

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 02.04.2011
Ответить


aspirant Перейти

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


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

aspirant

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


Mikhail Sukhov Перейти
skuvv Перейти

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


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


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

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

loading
clippy