воскресенье, 27 декабря 2009 г.

Merry Christmas and Happy New Year!

Вот, собственно, и все в этом году! Поздравляю всех трейдеров, которые хорошо потрудились в этом году за написанием своих торговых роботов, а так же за их эксплуатацией (считай, восторгом от прибыли и ужасанием от обытка). Новых версий до окончания праздников уже не будет. Но уже после окончания выйдет версия 1.5. Изменений и нововедений накопилось достаточно, плюс обещаю вполне жизненный пример, который можно взять за основу для своего робота.

Желаю всем хорошо отдохнуть, желаю и себе не оплошать.

С Новым Годом!

суббота, 12 декабря 2009 г.

Ecng.Trading 1.4

Обновление! Выложил новую версию 1.4.1

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

1. Стоп-заявки регистрируются, изменяются и снимаются через те же методы (здесь и далее будет идти речь об интерфейсе ITrader), что и обычные: RegisterOrder, ReRegisterOrder и CancelOrder.
2. Чтобы получить все стоп-заявки, можно вызвать свойство StopOrders. В принципе, можно это сделать и через свойство Orders, где хранятся как обычные, так и стоп-заявки. Но в этом случае придется отфильтровывать самостоятельно.
3. События NewStopOrders и ChangedStopOrders вызываются при появлении новых стоп-заявок, или когда они изменяются (активизируются, снимаются).
4. Метод GetDerivedOrder позволяет получить заявку, которая была создана стоп-заявкой (вызывать можно только для тех стоп-заявок, для которых Order.DerivedOrderId не равен null). Производная заявка появляется в системе тогда, когда активизируется стоп. Метод полезен тогда, когда необходимо узнать, какие из сделок были созданы в рамках стоп-заявки. Напрямую это узнать невозможно, так как стоп-заявки не существуют физически на биржах, и есть только связь между обычной заявкой и сделкой.

Традиционно, скриншоты с изменениями DDE (коснулось не только таблицы стоп-заявок, но и обычных):



Обновленный info.wnd файл лежит в архиве.

Как создавать и работать со стоп-заявками, я показал с своем примере, который расширил функционально:



Изменения коснулись и алгоритма котирования, который появился в версии 1.3 под названием MarketOrderRegistry. Идея мне понравилась, а вот реализация нет. Да так, что хотелось вырвать из блога пост и бросить его в печку. Что не понравилось и что переделал:

1. Название MarketOrderRegistry не отражает саму идею котирование. Поэтому теперь класс называется QuotingAlgo.
2. Котирование происходило только в одном режиме - рыночная цена. Теперь их четыре: по рыночной цене (причем эта опция так же разбивается на три под-опции), по последней сделке, по лучше цене, по лучшему объему. За подробностями в документацию по QuotingTypes.
3. Не была сделана парадигма торговых заданий, которую я придерживаюсь с самого начала. Теперь все задания котирования выделены отдельным классом QuotingTask. И этих самых тасков (=заданий) можно создавать неограниченное количество.

понедельник, 30 ноября 2009 г.

Ecng.Trading 1.3

Выложил новую версию Ecng.Trading v1.3. Скачивать отсюда. Теперь по этой ссылке я буду выкладывать все новые релизы.

В новой версии я добавил два, на мой взгляд, главных изменения.

Первое касается того, как экспортировать произвольные таблицы из Квика. В 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 можно написать логику лучше заявки (например, основываясь на ценовом или количественном объеме впереди в стакане).

среда, 18 ноября 2009 г.

Преимущества и недостатки разработки торговых роботов на разных платформах

Меня часто спрашивают, на чем лучше разрабатывать свое собственное МТС, какой язык программирования выбрать или чем моя библиотека Ecng.Trading отличается от других решений. Постараюсь ответить в этом, не побоюсь этого слова, опусе.

На данный момент можно выделить следующие группы ПО, где разрабатываются МТС:
1. Средствами внутреннего языка торгующей программы. Например, Quik и его QPILE. Преимущество такого такого подхода в том, что разработчики самой программы стараются максимально расширить свой внутренний язык, добавляя в него новые функции. Написанные скрипты получают данные значительно быстрее, нежели их получать из внешней программы (хотя это довольно спорное преимущество, потому как если написан алгоритм, который критично завязан на скорость исполнения, то такой робот лучше выносить в отдельное приложение, запускать его на сервере, и сервер подключать к торговому протоколу напрямую. Иначе и тормоза в работе, и проскальзование, и лосс). К недостаткам же можно отнести то, что скрипты работают не так быстро. Это и от того, что торговая система отдает преимущественно свои ресурсы своей основой задаче – торговле, и от того, что скриптовый язык разрабатывается под универсальные решения. А универсальность в ИТ мире редко когда идет рядом со скоростью.
2. Excel. Это просто мега система. По моему личному наблюдению, решения на Excel встречается практически во всех отраслях. Тут и бухгалтерия, и учет, и вычисление простых формул. Везде пробрался Excel, так что в инвестиционном бизнесе он не стоит особняком. Преимущество его использования кроется в банальном – на нем легко начать. Сравнительно простое создание формул, масса примеров, простой внутренний язык VBA – и вот уже на компьютере запущено некое решение на Excel. Минусы у данного подхода такие же как и у предыдущего пункта. К этому стоит добавить еще то, что на Excel не так уж тривиально интегрироваться с торговой программой, что для предыдущего пункта делать вообще не нужно. Отдельно можно выделить стабильность - всего один поток, и если он зависнет, зависнут как другие скрипты, так и не будет возможности перезапустить алгоритм (а Excel еще не научился вытаскивать себя из болота за волосы как Мюнхаузен), или даже перезапустить соединение с торгующей программой.
3. Аналитические программы (AmiBroker, WealthLab, MetaStock). Сами торговать не умеют, поэтому необходим некий адаптер подключения к торгующим программам (пункт 1). Преимущества и недостатки такие же как и у пункта 1 (я бы даже сказал, эти два подхода очень близки друг к другу). Дополнительным преимуществом является то, что аналитическая программа предоставляет для своего скрипта более богатый функционал. Плюс есть возможность подключать исторические данные для тестирования стратегий. Минусом является то, что, как и Excel, необходимо подключать к торговым системам, что еще более снижает производительность по сравнению с внутренним языком торгующей программы. И, как правило, нет возможности отследить потерю соединения.
4. Языки программирования (C#, VB, Java, Delphi). Основным плюсом такого подхода является, конечно же, скорость работы системы. Можно оптимизировать как сам алгоритм, использую любой подход, и сами формулы (кстати, формулы в большинстве случаев придется писать с нуля, потому что даже такая распространенная формула как скользящая средняя не будет присутствовать в языке; с другой стороны реализацию формул можно найти в Интернете, где можно найти и более сложные математические формулы, которые вряд ли будут присутствовать в аналитической системе), подстроив их работу под алгоритм, и соединение с торгующей программой. Особо критичные по скорости алгоритмы можно подключать к биржам напрямую, что дает преимущество в скорости отправки заявок и получения необходимой информации. Минусом является то, что из всех вышеперечисленных пунктов данный подход является самым сложным. Для начинающих в программировании я советую его использовать, когда уже есть готовая система, и ее хочется сделать стабильнее и производительнее. Но, еще раз предупреждаю, пройдет немало времени, прежде чем можно будет увидеть хороший результам (есть мнение, что можно этот самый хороший результат и вообще не увидеть – все зависит от знаний в программировании).

На фоне этих четырех пунктов моя библиотека Ecng.Trading относиться к пункту 4. Это именно библиотека для программирования под .NET (платформа содержит в себе языки C#, VB, C++, так что писать можно на любом из них). Преимущество и недостатки у нее точно такие, как и у данного пункта. Поэтому, основной целевой аудиторией при разработке библиотеки я сделал на людей, знакомых с программировании (трейдеров или программистов). Лично я программирую на языке C#, так как это самый перспективный и функциональный язык. Вот ссылки, которые я могу привести, чтобы начать программировать роботов с помощью Ecng.Trading:
1. Visual Studio – средство разработки, где пишется сама программа. Скачивать отсюда.
2. Электронный учебник по языку C# - отсюда.
3. Руководство использования Ecng.Trading - весь мой блог.

воскресенье, 15 ноября 2009 г.

Ecng.Trading v1.2

Обновление! Рекомендую после данного сообщения прочитать это.

Руки окончательно дошли не только до Ecng.Trading, чтобы его расширить, но и выложить в общий доступ. Качайте. Самое главное изменение в этой версии - появились новые свечки. Раньше были свечки, основанные только на тайм-фрейме, причем, жестко определенном: минутка, пяти-, и т.д.. Теперь тайм-фрейм можно задавать любой, через .NET класс TimeSpan (например, TimeSpan.FromMinutes(2) создает двухминутку). И свечки стали вида:

1. TimeFrameCandle - старая добрая тайм-фрейм свечка.
2. TickCandle - свечка, строящаяся на основе количества сделок.
3. VolumeCandle - свечка, строящаяся на основе допустимого объема.
4. RangeCandle - свечка, строящаяся на основе максимального отклонения цены сделки от открытия свечки.

Для свечек я добавил два события в ITrader: NewCandle (вызывается для свечек, которые только что начали формировать), CandlesChanged (вызывается для измененных свечек).

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

1. RegisterTimeFrameCandles - передается инструмент, и нужный тайм-фрейм. После вызова этого метода, робот начнет получать события NewCandle и CandlesChanged с объектами TimeFrameCandle.
2. RegisterTickCandles - то же самое, что и RegisterTimeFrameCandles, только уже для свечек TickCandle.
3. RegisterVolumeCandles - аналогично.
4. RegisterRangeCandles - аналогично.

И последнее, немаловажное изменение, касается времени последней сделки (я уже писал об этом, пункт 2). Теперь, QuikWrapper принимает по DDE сразу два параметра, "Время последней сделки" и "Время изменения". Вот как выглядит теперь таблица инструментов:



Все скриншоты настроек DDE и файл с настройка окон в Квике я выложил в том же архиве.

пятница, 6 ноября 2009 г.

Особенность построения МТС под Quik, использую Ecng.Trading

Довольно много пришло писем на почту (как-то народ не очень любит писать в комменты, вопрос - почему) с разного рода ошибками. Об ошибках, в которых повинен я, писать не буду, потому как я их буду устранять по мере появления. А вот о тех, которые я устранить не могу (например, особенности работы Квика), я напишу.

1. "System.BadImageFormatException was unhandled" (если установлен .NET Framework русской версии то сообщение будет такое "Была сделана попытка загрузить программу, имеющую неверный формат."). Эта ошибка говорит о том, что запускается программа как 64 битовое приложение. Библиотека же Trans2Quik.dll написана только под 32 битовые приложения. А на дворе уже 21 век, и найти не 64 битовые компьютеры все сложнее и сложнее. Поэтому и возникает подобная путаница. Поэтому, преждем чем начать создавать своего робота на .NET под Квик, нужно зайти в свойства проекта, раздел Build и выставить Platform target в x86:


2. При торговле на FORTS у Security всегда нулевое значение свойства LastTrade. Это происходит потому, что в таблице инструментов Квик для FORST не показывает время последней сделки. Но он умеет показывать время последнего изменения. Ecng.Trading не опирается на названия колонок в таблице при экспорте через DDE, но учитывает порядковый номер. Поэтому, чтобы получать значение последней сделки в Security, нужно заменить колонку "Время последней сделки" на "Время последнего изменения".

3. Неправильно настроены таблицы в Квике. Под пример, который идет в архиве вместе с Ecng.Trading, я выкладываю файл с настройками для Квика скачать. В нем есть различные фильтры, наподобие того, что показаны только A1 ММВБ и пара фьючерсов, но я думаю, отменить этот фильтр в Квике не составит труда.

4. Когда запускается экспорт Всех сделок через DDE, компьютер может подтормаживать. Это связанно с количеством сделок, которое грузится в первый раз (актуально, когда робот запускается не в начале торговой сессии). Так вот, в программе лучше прописать алгоритм так, чтобы он ждал пока все сделки не подгрузятся. Например, брать тот же Security.LastTrade и ждать, пока он не придет через событие ITrader.NewTrades. Или же заставить пользователя смотреть за процессором в Task Manager.

5. "Что умеет моя программа?". Ничего! Потому что у меня не программа. У меня библиотека с API для разработки этих самых программ. Скриншоты с мой якобы программой являются скриншотами примера, которого я создал специально продемонстрировать, что умеет моя библиотека.

пятница, 23 октября 2009 г.

Ecng.Trading - новое название проекта QuikWrapper

Обновление! Рекомендую после данного сообщения прочитать это.

Вот с такого названия пишу новое сообщение в блоге. Потому как вторая версия (качать отсюда) QuikWrapper-а получилась с настолько большим изменением, и настолько много я запрограммировал вспомогательных программ (утилит) с этой библиотекой, что по мере разработок новых и новых программ, я стал больше понимать, что нужно разделять мух от котлет. А именно, я решил разделить все, что относится к интернет-трейдингу и МТС, и конкретно Quik реализацию на разные библиотеки.

В последствии, я даже удивился, насколько это стало удобным. Например, у меня дома есть сервис с историческими данными. Эти данные я использую в программе тестирования стратегий. Так вот, очень удобно использовать Ecng.Trading для тестирования, но не очень удобно тянуть при этом Quik обертку. Зачем она, ведь я пока не торгую!? Вот и получилось, что написал я на C# с помощью библиотеки стратегию, подключил исторические данные, потестировал, подключил QuikWrapper, и уже начал торговать. И все эти шаги без переписывания кода. Я думаю, многие меня поймут, когда начинают писать прототип своей торговой системы сначала на одном языке, например на AmiBroker скрипте (или еще хуже, на Excel VBA), а затем переписывают на Delphi или .NET, так как нужна большая скорость и гибкость. Как в рекламе, зачем платить в два раза больше, если есть Ecng.Trading!

Изменение номер раз. Термины интернет-трейдинга (классы Security, Order, Trade и т.д.) перекочевали в сборку Ecng.Trading.BusinessEntities. Это полностью независимая сборка, и ее можно использовать везде, назависимо, разрабатывается ли программа под Quik или нет.

Изменение номер два. Добавились новые термины Candle, который представляет собой свечку, и Unit. Последнее собой представляет значения для хранения абсолютного или процентного значения. Поддерживаются базовые арифметически операции (+/-/*/:).

Изменение номер три. Появился интерфейс ITrader. Сам я создал только одну реализацию - QuikTrader, но если кто-то программирует под другие системы (например, Smart), пишите, попробуем добавить еще одну реализацию.

Изменение номер четыре. Базовые классы BaseTask и BaseTradeProcess для создания стратегий выделил в отдельную сборку - Ecng.Trading.Algo.

Изменение номер пять. В QuikTrader (который раньше назывался Trader) я добавил, во-первых, функционал по получению свечек, во-вторых, обработка стакана котировок, в-третьих, работа с DDE (запуск из своей программы DDE экспорт в Quik-e, которые, что важно отметить, само правильно выставляет найтроки в DDE окна Quik-а):

/// <summary>
/// Реализация интерфейса <see cref="ITrader"/>, предоставляющая шлюз взаимодействия с системой Quik.
/// </summary>
public class QuikTrader
{
/// <summary>
/// Инициализация класса <see cref="QuikTrader"/>.
/// </summary>
/// <param name="path">Путь к директории, где установлен Quik.</param>
/// <param name="ddeServer">Название DDE сервера.</param>
/// <param name="ddeWindowCaption">Заголовок окна экспорта через DDE в 
/// Quik-е. В русской версии окно имеет заголовок "Вывод через DDE сервер".</param>
/// <param name="securitiesTableCaption">Названия таблицы инструментов в Quik-е (в русской версии имеет название "Инструменты").
/// Необходимо DDE экспорту для поиска окон по заголовкам.</param>
/// <param name="tradesTableCaption">То же самое, что и securitiesTableCaption, только для всех сделок.</param>
/// <param name="ordersTableCaption">То же самое, что и securitiesTableCaption, только для заявок.</param>
/// <param name="myTradesTableCaption">То же самое, что и securitiesTableCaption, только для моих сделок.</param>
/// <param name="stockTableCaptionFormat">Формат заголовка онка с котировнками.</param>
public QuikTrader(string path, string ddeServer, string ddeWindowCaption,
string securitiesTableCaption, string tradesTableCaption,
string ordersTableCaption, string myTradesTableCaption,
string stockTableCaptionFormat);

/// <summary>
/// Запустить экспорт через DDE по всем таблицам (инструменты, сделки, заявки, мои сделки).
/// </summary>
public void StartDde();

/// <summary>
/// Запустить экспорт через DDE по таблицам, указанных параметром ddeTables.
/// </summary>
/// <example>
/// // запускаем экспорт по таблице инструментов и заявкам.
/// _trader.StartDde(DdeTables.Securities | DdeTables.Orders);
/// </example>
/// <param name="ddeTables">Таблицы, для которых необходимо запустить экспорт через DDE.</param>
public void StartDde(DdeTables ddeTables);

/// <summary>
/// Запустить для заданного инструмента экспорт через DDE стакана котировок.
/// </summary>
/// <param name="security">Инструмент, котировки которого необходимо выводить.</param>
public void StartDde(Security security);

/// <summary>
/// Остановить экспорт  DDE по всем таблицам (инструменты, сделки, заявки, мои сделки).
/// </summary>
public void StopDde();

/// <summary>
/// Остановить экспорт DDE по таблицам, указанных параметром ddeTables.
/// </summary>
/// <param name="ddeTables">Таблицы, для которых необходимо остановить экспорт через DDE.</param>
public void StopDde(DdeTables ddeTables);


/// <summary>
/// Остановить для заданного инструмента экспорт DDE стакана котировок.
/// </summary>
/// <param name="security">Инструмент, для которого необходимо остановить экспорт котировок.</param>
public void StopDde(Security security);
}


Изменение номер шесть, последнее. QuikTrader теперь поддерживает обработку по DDE стакана котировок, и получить эти котировки можно через метод GetQuotes.

Для всех нововведений приведу еще раз скриншотами настройки для DDE (новые, старые, изменившиеся):

Инструменты


Все сделки


Заявки


Мои сделки


Стакан (на примере, для LKOH)

Для закрепления того, о чем я писал выше, я создал программу-пример. На этот раз это визуальное приложение под Windows, которые представляет собой простейший привод для торговли:

пятница, 18 сентября 2009 г.

QuikWrapper

Обновление! Данный проект был полностью переделан. Описание нового проекта с документацией и дистрибутивом находиться по адресу http://stocksharp.com.

На днях я, наконец-то, разобрался с системой экспорта данных из 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 импорта транзакций"). В итоге получился следующий функционал:

/// <summary>
/// Основной класс, предоставляющий шлюз взаимодействия с Quik.
/// </summary>
public class Trader
{
/// <summary>
/// Инициализация класса Trader.
/// </summary>
/// <param name="path">Путь к директории, где установлен Quik.</param>
/// <param name="ddeServer">Название DDE сервера.</param>
public Trader(string path, string ddeServer);

/// <summary>
/// Событие изменения состояния подключения. Срабатывает при первом подключении программы к Quik-у,
/// сигнализирую о том, что соединение установлено.
/// </summary>
public event Action<Codes, QuikApiException> ConnectionChanged;

/// <summary>
/// Ошибка при обработке DDE данных, посланых Quik-ом.
/// </summary>
public event Action<Exception> DdeError;

/// <summary>
/// Событие появления собственных новых сделок.
/// </summary>
public event Action<IEnumerable<Trade>> NewMyTrades;

/// <summary>
/// Событие появления всех новых сделок.
/// </summary>
public event Action<IEnumerable<Trade>> NewTrades;

/// <summary>
/// Событие появления новых заявок.
/// </summary>
public event Action<IEnumerable<Order>> NewOrders;

/// <summary>
/// Событие изменения состояния заявки (снята, удовлетворена).
/// </summary>
public event Action<IEnumerable<Order>> OrdersChanged;

/// <summary>
/// Событие загрузки данных по инструментам.
/// </summary>
public event Action SecuritiesLoaded;

/// <summary>
/// Список всех загруженных инструментов.
/// Вызывать только после того, как пришло событие <see cref="SecuritiesLoaded" />.
/// </summary>
public IEnumerable<Security> Securities;

/// <summary>
/// Получить биржевое время.
/// </summary>
public DateTime StockTime;

/// <summary>
/// Получить все зявки, которые были зарегистрированный программой через метод <see cref="RegisterOrder" />.
/// </summary>
public IEnumerable<Order> Orders;

/// <summary>
/// Получить мои сделки.
/// </summary>
/// <param name="security">Инструмент, по которому нужно найти сделки.</param>
/// <param name="from">Дата, с которой нужно искать сделки.</param>
/// <param name="to">Дата, до которой нужно искать сделки.</param>
/// <returns>Найденные сделки.</returns>
public IEnumerable<Trade> GetMyTrades(Security security, DateTime from, DateTime to);

/// <summary>
/// Получить все сделки.
/// </summary>
/// <param name="security">Инструмент, по которому нужно найти сделки.</param>
/// <param name="from">Дата, с которой нужно искать сделки.</param>
/// <param name="to">Дата, до которой нужно искать сделки.</param>
/// <returns>Найденные сделки.</returns>
public IEnumerable<Trade> GetTrades(Security security, DateTime from, DateTime to);


/// <summary>
/// Получить стакан котировок.
/// </summary>
/// <param name="security">Инструмент, по которому нужно получить котировки.</param>
/// <returns>Найденные котировки. Если для инструмента нет котировок, то возвращается пустой список.</returns>
public IEnumerable<Quote> GetStock(Security security);

/// <summary>
/// Зарегистрировать заявку на бирже.
/// </summary>
/// <param name="order">Заявка, содержащая информацию для регистрации.</param>
public void RegisterOrder(Order order);

/// <summary>
/// Отменить заявку на бирже.
/// </summary>
/// <param name="order">Заявка, которую нужно отменять.</param>
public void CancelOrder(Order order)

/// <summary>
/// Получить заявку по сделке.
/// </summary>
/// <param name="trade">Сделка, по которой нужно искать заявку.</param>
/// <returns>Найденная заявка.</returns>
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.