В новой версии я добавил два, на мой взгляд, главных изменения.
Первое касается того, как экспортировать произвольные таблицы из Квика. В QuikTrader появилось событие ProcessDdeData. Данное событие вызывается тогда, когда Квик послал DDE данные, которые QuikTrader не умеет обрабатывать. В качестве демонстрации я добавил в свое приложением-пример отображение данных по портфелю. Сначала я настроил Квик, добавив в него соответствующую таблицу:
Обновленные info.wld файл идет вместе с архивом.
Затем, добавил в него код обработки и отображения данных:
_trader.ProcessDdeData += (name, rows) => { // узнаем, что пришедшие данные отвечают за портфель if (string.Compare(name, "portfolio", true) == 0) { foreach (var row in rows) { var client = (string)row[0]; var portfolio = _portfolioWindow.Portfolios.FirstOrDefault(p => p.Client == client); if (portfolio == null) { portfolio = new Portfolio { Client = client }; _portfolioWindow.Portfolios.Add(portfolio); } portfolio.Shorts = (double)row[1]; portfolio.Longs = (double)row[2]; portfolio.Collateral = (double)row[3]; portfolio.Margin = (double)row[4]; portfolio.Money = (double)row[5]; portfolio.PnL = (double)row[6]; } } };
Так как QuikTrader уже содержит методы по запуску и остановке DDE экспорта (StartDde и StopDde), то для удобства я перегрузил этим методы, чтобы они могли принимать имя экспортируемой таблицы (имя отображается в заголовке таблицы в Квике). Это позволит не только управлять своим потоком данных, но еще и запускать и останавливать его. Так что, если кто-то еще разрабатывает роботов под Excel, руководствуясь тем, что в него можно передавать данные как угодно, знайте, теперь это можно делать и в Ecng.Trading. Ну, а про преимущества разработки роботов на C# по сравнению с Excel я уже писал здесь.
Второе изменение знаковое. Наконец-то, в Ecng.Trading появился первый торговый алгоритм - котирование заявок. Например, необходимо срочно закрыть позицию, продав или купив по рынку (при минимуме потере профита). При высоколиквидном инструменте посланная из Квика или робота заявка может дойти до рынка уже "неактуальной" (в принципе, можно выставлять и из Квика рыночную заявку, но она работает не на всех биржах). Чтобы решить эту проблему, я написал класс MarketOrderRegistry, в который добавляется заявка. Заявка может быть и как уже ранее зарегистрированная, так и "пустая" (только что созданная и еще не выпущенная на биржу). И уже этот класс двигает эту заявку в стакане так, чтобы продать ее по выгодной рыночной цене. Рыночная цена опирается на текущий BestBid и BestAsk инструмента. Если же задана MarketDelta (передается в метод AddOrder), то цена заявки будет высчитываться как смещение цены последней сделки на эту самую MarketDelta.
Другое применения MarketOrderRegistry - это скальперская стратегия. Класс может сам создавать заявки и контролировать их в стакане.
Сам по себе класс MarketOrderRegistry построен на основе методов из класса TraderHelper:
1. GuarantyCancelOrder - гарантированно отменить заявку. В цикле посылает команду CancelOrder до тех пор, пока заявка не снимется (или не исполниться, если робот не успел снять).
2. ReRegisterOrder - перерегистрировать заявку. Удобен тем, что умеет подстраиваться под особенности биржи. Например, FORTS умеет изменять заявки одной транзакцией. Тогда в метод нужно передавать параметр isForts равный true. Если биржа не поддерживает изменения заявки одной транзакцией, то метод последовательно сначала снимает заявку через GuarantyCancelOrder, а затем регистрирует новую.
В принципе, можно реализовать своего собственного котировщика, вызывая методы TraderHelper. Как я уже упоминал, для той же скальперской стратегии. Сам по себе MarketOrderRegistry не смотрит на то, действительно ли нужно переставлять заявку. Он лишь старается выставить ее на край спреда. Поэтому, низкоуровневый класс TraderHelper для тех, кто реализует скальпинг, будет более полезен, чем сам MarketOrderRegistry. Используя TraderHelper можно написать логику лучше заявки (например, основываясь на ценовом или количественном объеме впереди в стакане).
Спасибо, большое.
ОтветитьУдалитьСмотрел Order.Condition
ОтветитьУдалитьВот фрагмент кода, подправте пожалуйста если что не так
//Создаем стоп-заявку на покупку
Order order = new Order
{
Price = _settings.Security.BestAsk,
Account = _settings.Account,
Type = OrderTypes.Conditional,
Volume = _settings.Volume,
Direction = OrderDirections.Buy,
Security = _settings.Security
};
order.Condition.StopPrice = _settings.Security.BestAsk+2;
Этот комментарий был удален автором.
ОтветитьУдалитьДа, закралась ошибка. Библиотека должна отделять тейк от стоп-лимита по цене (та, которая Order.Price но не Order.Condition.StopPrice). Если указана, то лимит, иначе тейк. Но не определяет. Выложу новую версию через несколько часов в тот же архив.
ОтветитьУдалитьОбновил архив с версией 1.3. Попутно сделал некоторые небольшие и большие изменения. Напишу о них по позднее.
ОтветитьУдалитьЗдравствуйте, а можно посмотреть реализацию дде сервера... никак понять не могу как с ним работать) Спасибо
ОтветитьУдалитьMika, я заметил, что отправка транзакций по API синхронные, а есть ли возможность посылать асинхронные заявки?
ОтветитьУдалитьMika.
ОтветитьУдалитьЯ обновил билиотеки, но все еще проблема существует.
Вот фрагмент кода, подправте пожалуйста если что не так
//Создаем стоп-заявку на покупку
Order order = new Order
{
Price = _settings.Security.BestAsk,
Account = _settings.Account,
Type = OrderTypes.Conditional,
Volume = _settings.Volume,
Direction = OrderDirections.Buy,
Security = _settings.Security
};
order.Condition.StopPrice = _settings.Security.BestAsk+2;
А вот строка транзакции
ACCOUNT=ХХХХХХХХХ; CLIENT_CODE=XXX; TRANS_ID=1; CLASSCODE=SPBFUT; SECCODE=SRZ9; ACTION=NEW_STOP_ORDER; OPERATION=S; QUANTITY=1; STOPPRICE=7191; PRICE=7193; STOP_ORDER_KIND=TAKE_PROFIT_STOP_ORDER;
В строке транзакции должны быть и STOPPRICE=7191; и PRICE=7193;
ОтветитьУдалитьА не лудше програмисту решать какой тип сделки он хочет и самому выбирать STOP_ORDER_KIND?
HaMMeR,
ОтветитьУдалитьВозьмите еще раз послед. версию. Накладка получилась в спешке - перезалил старые dll. Теперь все работает, в частности Ваш примерно у меня прошел.
Gazrvs,
ОтветитьУдалитьЛегко. Пользуемся мощными .NET средствами:
var order = new Order();
// создаем делегат
Action action = () => _trader.RegisterOrder(order);
// вызываем его асинхронно
action.BeginInvoke(result =>
{
// когда мы тут, то заявка уже зарегистрированна
Console.WriteLine(order.Id);
// очищаем хэндл асинхронного вызова
action.EndInvoke(result);
}, null);
Serg,
ОтветитьУдалитьВ примере этого поста я как раз и показал, как с ним работать.
Mika.
ОтветитьУдалитьСпасибо, что подправили Стоп-Заявки.
Но метод
public override void CancelOrder( Ecng.Trading.BusinessEntities.Order order)
Member of Ecng.Trading.QuikWrapper.QuikTrader
не умеет распознавать и снимать Стоп заявки.
HaMMeR,
ОтветитьУдалитьВ ближайшее время (до четверга) выложу следующую версию. Там будет полная поддержка стоп-заявок (регистрация, снятие, изменение). В том числе и DDE. В том числе и события NewStopOrders, ChangedStopOrders. В том числе и так же возможность получить дочерние заявки по родительской стоп-заявке.
Доброе время суток!
ОтветитьУдалитьПопробовал запустить пример с экспортом всех сделок и получил такую ошибку
Не удалось привести тип объекта "System.Double" к типу "System.String".
Ecng.Trading.QuikWrapper
в Ecng.Trading.QuikWrapper.QuikTrader.☻(String ☻, IList`1 ♥)
в ☻.♥()
Что можно сделать?
Запускаю с версией quik 5.14, Vista, Framework 3.5
Попробовал еще экспортировать список инструментов и получил такое
ОтветитьУдалитьЗаданное приведение является недопустимым.
Ecng.Trading.QuikWrapper
в Ecng.Trading.QuikWrapper.QuikTrader.♠.☻(String ☻)
в Ecng.Collections.CollectionHelper.SafeAdd[K,V](IDictionary`2 dictionary, K
key, Func`2 handler)
в Ecng.Trading.QuikWrapper.QuikTrader.☻(String ☻, IList`1 ♥)
в ☻.♥()
Leonandr, Андрей Леонтьев,
ОтветитьУдалитьВидимо, таблицы в Квике настроены не так, как в нужно. Посмотрите скриншоты, которые идут в архиве. Все ли у вас колонки? В правильно ли порядке?
да, загрузил файл info.wnd и все заработало. А в чем секрет? только в наличии таблиц и нужных колонок или еще где-то есть какие-то важные настройки квика?
ОтветитьУдалитьтолько в наличии таблиц и нужных колонок
ОтветитьУдалитьТут уже писали про исключение The service is not registered. Оно появляется тут 9 из 10 раз.
ОтветитьУдалитьtrader = new QuikTrader(PATH_2_QUIK, "wrapper",
"Вывод через DDE сервер", "инструменты", "все сделки", "заявки", "мои сделки", "{0} котировки");
Очень странно и непонятно. Как будто при подключении к квику, приложение вылетает по таймауту.
Leonandr,
ОтветитьУдалитьВерсия последняя?
да, версия самая последняя.
ОтветитьУдалитьОшибка появляется, если я вешаю обработчик на ConnectionChanged
trader = new QuikTrader(PATH_2_QUIK, "wrapper",
"Вывод через DDE сервер", "инструменты", "все сделки", "заявки", "мои сделки", "{0} котировки");
trader.ConnectionChanged += connChanged => Sync(() =>
{
String m = connChanged.Message;
});
А что пишет, если подписаться на DdeError?
ОтветитьУдалитьвсе работает, прошу прощения.
ОтветитьУдалитьДобрый вечер.
ОтветитьУдалитьТеперь другая проблема. Экспортирую стакан котировок, а при чтении его в программе, в поле Volume, если OrderDirection = Sell, всегда стоит 0.
Наверное неправильно настроил таблицу стакана, но непонятно как сделать это правильно.
А кспортируете как? Сами, через ProcessDdeData или через ITrader.GetQuotes? Во втором случае, скриншот с правильной настройкой лежит в архиве. Называется quote_dde.png
ОтветитьУдалитьЭкспортирую через ITrader.GetQuotes. Но все равно поле volume равно 0 для продажи. А для покупки все нормально. Очень странно.
ОтветитьУдалитьК тому же, мне кажется, что перепутаны sell и buy.
Вот как у меня выводится.
Price Volume Direction
1696,16 0 Sell
1696,17 0 Sell
1696,18 0 Sell
1697 0 Sell
1697,2 0 Sell
1697,57 0 Sell
1698 0 Sell
1698,22 0 Sell
1698,36 0 Sell
1699 0 Sell
1699,02 13 Buy
1699,33 1 Buy
1700 11 Buy
1700,06 1 Buy
1700,13 25 Buy
1700,14 133 Buy
1700,15 73 Buy
1700,21 12 Buy
1700,26 66 Buy
1700,27 18 Buy
А стакан настроен как на картинке?
ОтветитьУдалитьда, как на картинке
ОтветитьУдалитьMika, вы пользуетесь ли каким нибудь дополнительным приложением(или плагином)к Visual Studio для работы с WPF, что то запутался с привязкой данных кода к новому окну WPF!?
ОтветитьУдалитьА когда будет полная поддержка Стоп-заявок?
ОтветитьУдалитьHaMMeR, выложил. Описание будет чуть позже. В DDE поменялась таблица Заявки и появилась новая Стоп-Заявки. Все отражено на скриншотах.
ОтветитьУдалитьLeonandr,
ОтветитьУдалитьПопробуйте с теми стаканами, что уже созданы в моем wnd файле.
Gazrvs,
ОтветитьУдалитьWPF - это граф. библиотека. Дополнительно использую использую WPF Toolkit. Последнее никокого оотношения к привязке данных (binding) не имеет, только новые контролы. В новом примере я использую из WPF Toolkit DatePicket контрол для выбора срока действия стоп-заявки.
Mika,
ОтветитьУдалитьК сожалению не объемы с Sell не выводятся ни с какими стаканами (даже с теми, что присутствуют в вашем файле). Уже какими только способами не пробовал, ничего не помогает. Жаль, что в примере нет окна с котировками, очень помогло бы. Во всяком случае я поверил бы, что проблема именно в моем коде.
Leonandr,
ОтветитьУдалитьпришлите скриншот с настройками таблицы (не ДДЕ, а там где настраиваются какие колонки должны присутствовать, название и т.д.)