На днях я, наконец-то, разобрался с системой экспорта данных из Quik по DDE (результаты качать отсюда). Парадокс построения МТС на базе Quik таков, что довольно легко отправить в него данные (заявки), но сложно получить что-то обратно (информацию по инструментам, тикам и т.д.). Стандартно механизм отдачи данных внешней программе построен на специальных адаптерах (для AmiBroker и Wealth-Lab идут стандартно в поставке), но если нужно действительно автономное решение (программирование роботов на C#, Java или Delphi), то начинаются танцы с бубном. Для сведения, протокол DDE - это старая технология. Настолько старая, что мало того, на данный момент в новых средах программирования (.NET или Java) не существует стандартных компонентов для работы с DDE, так еще и описаний в инете практически не найти.
В принципе, экспорт данных из Quik можно настроить через ODBC. Но у него есть ряд своих недостатков. Во-первых, на компьютере должна быть установлена база данных (мой выбор, MS Sql Server 2008 Express - быстр, стабилен, много возможностей, и, конечно же, бесплатен). Во-вторых, это медленная передача данных. В отличие от DDE, где данные передаются напрямую от Quik к сторонней программе, использование промежуточной базы данных сильно затормаживает и скорость самой МТС.
Есть еще последний вариант, когда можно написать МТС внутри Quik-а на языке QPile, но я его не рассматривал, так как это очень медленное решение, вдобавок и ограниченное возможностями самого языка (на дворе 21-ый век, и нужно использовать компьютеризированные возможности по максимуму).
Собственно, для того, чтобы каждый раз новичкам не проходить тернистый путь написания МТС на языке C# (или любом другом под платформу .NET), я и написал библиотеку QuikWraper. За основу был взят пример работы с Quik API (скачать можно отсюда http://www.quik.ru/user/download/, называется "API импорта транзакций"). В итоге получился следующий функционал:
////// Основной класс, предоставляющий шлюз взаимодействия с Quik. /// public class Trader { ////// Инициализация класса Trader. /// /// Путь к директории, где установлен Quik./// Название DDE сервера.public Trader(string path, string ddeServer); ////// Событие изменения состояния подключения. Срабатывает при первом подключении программы к Quik-у, /// сигнализирую о том, что соединение установлено. /// public event ActionConnectionChanged; /// /// Ошибка при обработке DDE данных, посланых Quik-ом. /// public event ActionDdeError; /// /// Событие появления собственных новых сделок. /// public event Action> NewMyTrades; /// /// Событие появления всех новых сделок. /// public event Action> NewTrades; /// /// Событие появления новых заявок. /// public event Action> NewOrders; /// /// Событие изменения состояния заявки (снята, удовлетворена). /// public event Action> OrdersChanged; /// /// Событие загрузки данных по инструментам. /// public event Action SecuritiesLoaded; ////// Список всех загруженных инструментов. /// Вызывать только после того, как пришло событие public IEnumerable. /// Securities; /// /// Получить биржевое время. /// public DateTime StockTime; ////// Получить все зявки, которые были зарегистрированный программой через метод public IEnumerable. /// Orders; /// /// Получить мои сделки. /// /// Инструмент, по которому нужно найти сделки./// Дата, с которой нужно искать сделки./// Дата, до которой нужно искать сделки.///Найденные сделки. public IEnumerableGetMyTrades(Security security, DateTime from, DateTime to); /// /// Получить все сделки. /// /// Инструмент, по которому нужно найти сделки./// Дата, с которой нужно искать сделки./// Дата, до которой нужно искать сделки.///Найденные сделки. public IEnumerableGetTrades(Security security, DateTime from, DateTime to); /// /// Получить стакан котировок. /// /// Инструмент, по которому нужно получить котировки.///Найденные котировки. Если для инструмента нет котировок, то возвращается пустой список. public IEnumerableGetStock(Security security); ////// Зарегистрировать заявку на бирже. /// /// Заявка, содержащая информацию для регистрации.public void RegisterOrder(Order order); ////// Отменить заявку на бирже. /// /// Заявка, которую нужно отменять.public void CancelOrder(Order order) ////// Получить заявку по сделке. /// /// Сделка, по которой нужно искать заявку.///Найденная заявка. public Order GetOrder(Trade trade); }
В качестве теста я создал консольное приложение. Программа находит бумагу Лукойл, запоминает первоначальное значение середины спреда равное (bid + ask) / 2. Далее, как только значение спреда отклониться на 0.1 %, то выставляется заявка на покупку объемом 1 и ценой текущего спреда. Далее, если произойдет сделка по выставленной заявке, то программа выведет информацию по этой сделке. Вот текст программы:
// для теста выбираем бумагу Лукойл var secCode = "LKOH"; Security lkoh = null; // номер счета var account = "XXX"; var waitHandle = new ManualResetEvent(false); // создаем соединение с Quik-ом using (var trader = new Trader(@"D:\QUIK5", "wrapper")) { // подписываемся на событие появление инструментов trader.SecuritiesLoaded += () => { // находим Лукойл и присваиваем ее переменной lkoh lkoh = trader.Securities.First(sec => sec.Code == secCode); Console.WriteLine("Инструмент Лукойл появился"); waitHandle.Set(); }; // подписываемся на событие появления моих новых сделок trader.NewMyTrades += trades => { foreach (var trade in trades) Console.WriteLine("Сделка {0} по цене {1} по бумаге {2} по объему {3} в {4}", trade.Id, trade.Price, trade.Security.Code, trade.Volume, trade.Time); }; Console.WriteLine("Дожидаемся появления в программе инструмента Лукойл"); waitHandle.WaitOne(); if (lkoh != null) { // 0.1% от изменения цены var delta = 0.001; // запоминаем первоначальное значение спреда var firstMid = lkoh.BidAsk.Mid; Console.WriteLine("Первоначальное значение спреда {0}", firstMid); while (true) { // если спред вышел за пределы нашего диапазона if ( ((firstMid + firstMid * delta) <= lkoh.BidAsk.Mid) || ((firstMid - firstMid * delta) >= lkoh.BidAsk.Mid) ) { var order = new Order { Account = account, Price = lkoh.BidAsk.Mid, Security = lkoh, Volume = 1, Direction = OrderDirections.Buy, }; trader.RegisterOrder(order); Console.WriteLine("Заявка {0} зарегистрирована", order.Id); break; } else Console.WriteLine("Текущее значение спреда {0}", lkoh.BidAsk.Mid); // ждем 1 секунду Thread.Sleep(1000); } } else Console.WriteLine("Инструмент Лукойл не появился"); Console.WriteLine("Заканчиваем работу. Нажмите кнопку чтобы продолжить"); Console.Read(); }
Последнее, о чем необходимо еще упомянуть - это настройка самого Quik. Процесс несколько нудный, но, к счастью, делать нужно один раз. Самое первое, включить поддержку Внешних Транзакций. Пункт меню Торговля -> Внешние Транзакции и сделать так, как показано на рисунке:

И теперь DDE:
Инструменты

Все сделки

Заявки

Мои сделки

Важно! Колонки в таблицах должны идти так, как показаны на рисунках. Можно добавлять свои собственные колонки в конец, но никак не перемешивать с нужными. Запускать вывод через DDE тоже необходимо делать в строго определенном порядке. Сначала инструменты, затем все сделки, затем заявки, затем мои сделки.
Исходники примера, а так же сама библиотека с документацией располагается здесь:
http://www.box.net/shared/o7et6ac56x.