Первый пост о том как расширять функционал библиотеки. В нём я описал процесс создания нового подключения к библиотеке. Дерзайте, товарищи программисты. Пробуйте вносить новые сервера. Каждый новый сервер это вызов конечно, но на то я и здесь. Пишите, задавайте вопросы.
Коннектор в Os.Engine это не тоже самое что коннектор в СтокШарп. Коннектором здесь называется класс инкапсулирующий в себе обмен данными между BotTabSimple/BotTabIndex и определённой реализацией сервера(IServer). IServer в свою очередь это и есть то, что нужно делать чтобы добавить в систему новый тип подключения.
1 Сервер ( IServer) - сервер подключения к какой-либо бирже. Реализует в себе обмен данными между Os.Engine и биржей. Может быть развёрнут только один сервер одного типа. Могут быть развёрнуты множество серверов к разным подключениям. Например одновременно можно развернуть сервер Плаза 2 и Квик и т.д.
2 ServerMaster - класс для менеджмента реализации Серверов.
3 ServerMasterUi - окно менеджмента Серверов.
4 Коннектор - (Connector) - класс для подключения BotTabSimple/BotTabIndex к уже развёрнутому и готовому IServer. Connector есть у каждого бота. По нескольку штук бывает, когда роботу нужно получить несколько бумаг.
BotTabSimple/BotTabIndex - вкладки у робота. Через BotTabSimple осуществляется торговля. Эта вкладка специально созданная для торговли одного инструмента. Если нужно 2, то создаём две BotTabSimple. Вкладки храняться в BotPanel.
BotPanel - интерфейс создания робота. Это то что мы перегружаем в PanelCreator чтобы создать робота. Отвечает за менеджмент вкладок.
Сервер для платформы это вот этот интерфейс
Этот интерфейс используется:
1) в ServerMaster, где создаются подключения.
2) в Connector, объекте который представляет для роботов данные для торговли.
И создаём в этой вкладке соответствующий Класс.
Вот так вот:
namespace OsEngine.Market.Servers.NewFolder1
{
class NewServer
{
}
}
Затем сюда надо подключить интерфейс IServer:
namespace OsEngine.Market.Servers.NewFolder1
{
class NewServer: IServer
{
}
}
После этого компилятор начнёт нас ругать, т.к. по его мнению не все элементы на месте. Не будем с ним спорить, и жмём слева красную лампочку(если у Вашей студии нет ReSharper то останавливаемся, идём и ищем в интернет) и позволяем ReSharper добавить все элементы этого интерфейса к нам в класс.
После этого соглашаемся на всё и у нас появяться 120 строк с не реализованными функциями.
Эти методы, поля и события надо оживить. Надо чтобы все эти методы работали как нужно. Как в других серверах.
В нём есть структура и мы её забираем.
Во первых: из класса IServer переносим все названия методов, событий и полей - в новый класс. Это поможет понять что и за что отвечает.
Во вторых: В IServer кроме комментариев код разбит на блоки. Делаем точно также. Эти блоки будут далее помогать нам разбить работу на этапы. Это и есть наши этапы.
Переписывайте комментарии и разбиваем код на логические блоки. Вот в таком же порядке как в IServer.
Прежде чем писать свою реализацию, надо изучить работу этого сервера. И в дальнейшем ровняться именно на эту реализацию.
Смотрим блоками.
Кроме блоков которые есть в IServer здесь ещё есть два дополнительных:
// работа основного потока !!!!!!
// работа потока рассылки !!!!!
Эти блоки - краеугольный камень любого коннектора. Но об этом ниже.
Прямо регистрируйтесь на учебный сервер ItInvest и подключите его. Посмотрите как он работает. Поторгуйте. Разберите через какие методы он получает данные, через какие высылает. В общем. Потратьте на это пару дней.
Сервер разделён на логические блоки. И в таком же порядке их надо делать. Не ленитесь плз. Начинайте с разметки и потом спокойно блок за блоком заставляйте коннектор оживать.
В этой главе опишу каждый из этих блоков как смогу. Но лучше смотреть в код. Реализация есть в других серверах. Я рекомендую воровать у СмартКом. SmartComServer
В любом сервере есть три потока. Они создаются в конструкторе.
Поток 1 - занимается обработкой запросов пользователя к серверу. Как то подключается,/отключается, требует бумаги от коннектора и проч. Для чего: нельзя трогать сторонние Апи из разных потоков. Ни разу я не встречал чтобы Апи было потокобезопасным. Поэтому все запросы и действия надо делать через Флаги. Смотря на которые, этот поток будет совершать действия с Апи. Блок: "Работа основного потока"
Поток 2 - поток высылающий наверх входящие данные. Когда из сервера приходят новые данные, эти данные нельзя сразу отправить наверх. Данные нужно ставить в очереди(ConcurrentQueue). Далее поток2 в бесконечном цикле проверяет очереди данных и если что-то есть - высылает наверх через события. Блок: " работа потока рассылки !!!!!"
Поток 3 - поток обрабатывающий Ордера которые нужно снять или выставить. Точно так же. Нельзя напрямую зайти в этот класс через метод и тут же отправить заявку в терминал. Это надо делать через очередь. Блок: исполнение ордеров
Важно!
Поток 1 и поток 3 не должны ни в коем случае получить доступ к Апи в один момент времени. Поэтому надо использовать блокировку многопоточного доступа к нему. Реализацию смотрим в SmartComServer. Метод ExecutorOrdersThreadArea и PrimeThreadArea
lock (_smartComServerLocker)
{
// Внутри этого блока работает только поток 1 в ExecutorOrdersThreadArea
// или поток 3 в ExecutorOrdersThreadArea
// одновременно они сюда не попадают
}
Это первый блок который надо реализовать. В момент его создания мы:
1) даём серверу название. это поле: public ServerType ServerType
2) в перечисление ServerType нужно добавить новый тип сервера. Допустим у нас это будет "NewServer"
3) выделяем настройки для коннектора. Обычно это логин и пароль. Делаем методы Load() и Save().
4) делаем конструктор класса. в конструкторе присваиваем серверу наше имя и вызываем Load(). Смотрим SmartCom. Там ещё много чего.
Текущий статус сервера. Весь блок можно своровать из SmartComServer. Работает всё очень просто. Изначально надо установить статус в Disconnect. Затем, когда придёт коллбек во время соединения, нужно изменить статус и сетор ServerStatus сам вышлет оповещение наверх.
На колбек для статуса подписываемся из потока1.
Это один из двух блоков которых нет в IServer. Здесь у нас происходит важная работа.
Создаём метод:
PrimeThreadArea(). Внутри него бесконечный цикл с ожиданием 1 секунда. И посылаем в этот метод новый поток из конструктора.
Здесь, ориентируясь на статус сервера и флаги от пользователя мы:
1) создаём подключение Апи
2) пробуем к нему подключиться если пользователь запросил
3) создаём менеджер свечей, если его ещё нет
4) запрашиваем бумаги у Апи, если их ещё нет
5) запрашиваем портфели у Апи, запрашиваем их если ещё нет
6) подписываемся на обновления бумаг, портфеля, ордеров, моих Сделок(MyTrade). Свечки, тики и стаканы подписываются в другом месте! Только те которые нужны.
7) отключаемся от всего этого хозяйства, если пользователь запросил.
Иногда, этот же поток у меня рассылает и заявки. Таким образом я делал в ASTS Bridge и далее буду так делать. Но это не принципиально.
Два метода: StartServer и StopServer. Через них мы указываем влаг о желании пользователя. Что он хочет. Через них не нужно подключаться к Апи!
Эти флаги читает основной поток из PrimeThreadArea и уже делает всё сам.
Из метода SenderThreadArea поток следит за очередями, и если есть какие-то данные то он высылает их на верх через события.
В очереди данные нужно ложить из Коллбеков Апи. Нельзя вызывать события с данными нигде!!! кроме метода SenderThreadArea.
Надо просто копировать реализацию этого блока из SmartComServer. В ней всё идеально.
Когда заносяться данные в ServerTime, то если время новое, то у нас время заноситься в очередь на отправку.
Устанавливать ServerTime можно непосредственно подписавшись на время сервера. Если такого коллбека нет, то устанавливаем ServerTime во время приёма тиков из Апи. SmartServer_AddTick
Приём и хранение портфелей и позиции по ним.
На портфель подписывается поток1, в момент подключения.
Тут реализация раз от раза отличается, скопировать врятли удастся.
Позиция храниться в портфеле. Смотрим класс OsEngine.Entity.Portfolio и OsEngine.Entity. PositionOnBoard
В общем случае требуется следущее:
1) нужно портфели принять, сохранить и поставить в очередь на отправку.
2) нужно позиции принять, прогрузить ими портфели, сохранить и поставить в очередь на отправку.
3) оживить метод GetPortfolioForName(string name). Тут не будет проблем.
4) оживить public List Portfolios. По этому запросу нужно выдавать все(без дублей и проч.) ранее принятые портфели.
Приём и хранение инструментов.
На бумаги подписывается поток1, в момент подключения.
Тут реализация раз от раза отличается, скопировать врятли удастся.
Класс: OsEngine.Entity. Security
В общем случае требуется следущее:
1) нужно бумаги принять, сохранить и поставить в очередь на отправку.
3) оживить метод GetSecurityForName(string name)
4) оживить public List Securities. По этому запросу нужно выдавать все(без дублей и проч.) ранее принятые бумаги.
Так же раз от раза приходиться делать по разному.
В общем случае требуется:
1) оживить вот этот метод CandleSeries StartThisSecurity(string namePaper, TimeFrame timeFrame) . Со всеми костылями из СмартКОМ!!!
Так чтобы после этого CandleSeries начал выдвать свечи по этому инструменту и ТаймФрейму. Кроме того в этом методе нужно подписаться на тики и стакан этого инструмента.
CandleManager - в каждом сервере есть эта штука. Она отвечает за создание свечек из ТИКОВ. Эта штука создаётся в конструкторе сервера. Чтобы она начала создавать свечи по какомуто инструменту, в неё надо передать CandleSeries.
CandleSeries - место сбора конкретного ТФ свечек по конкретному инструменту.
Смотрим OsEngine.Entity.CandleManager OsEngine.Entity.CandleSeries. И реализацию StartThisSecurity в SmartComServer.
Подписываемся на стакан в предыдущем блоке. Здесь его принимаем, разбираем и ставим в очередь на рассылку.
Подписываемся на тики определённого инструмента в блоке "Подпись на данные".
Здесь их принимаем, разбираем, сохраняем и ставим в очередь на рассылку.
+ Оживляем List[] AllTrades.
Отдельно следует отметить, что если Апи не выдаёт тики хотябы за 5 дней, то появляется необходимость сохранять тики на своей стороне. Реализацию этой штуки нужно смотреть в этом же блоке но в сервере QuikServer. За это отвечает класс: OsEngine.Market.Servers.ServerTickStorage
На событие моих сделок подписывается поток1, в момент подключения к Апи.
Здесь их принимаем, разбираем, сохраняем и ставим в очередь на рассылку.
оживляем public List MyTrades.
В этом блоке две части. Одна принимает ордера, вторая для их выставления/снятия.
Блок один. Приём ордеров из Апи
Подписываемся на это в потоке 1.
Принимаем ордера которые приходят через апи, разбираем, сохраняем/обновляем, ставим в очередь на отправку.
Блок два. Исполнение ордеров.
Здесь работает поток номбер 3, в методе ExecutorOrdersThreadArea. Исполняет / отзывает ордера если пользователь такоевые передал ему через методы ExecuteOrder/CanselOrder.
Ничего не исполняется на прямую! Только через очереди. Не забываем про многопоточный доступ к Апи и lock. Про это есть в разделе про потоки.
Копируем полностью из СмартКом этот блок. _logMaster создаётся в конструкторе. Обычно я делаю это в последнюю очередь, т.к. удобнее при написании кода чтобы всё падало прямо и VS отправляет к месту проишествия. Вы не как хотите.
На входе моей сделки нужно проверять, не пришла ли она раньше ордера, т.к. для архитектуры это важно.
Поэтому когда входят ордера, мы проверяем массив с моими сделаками и дополнительно рассылаем их, чтобы исключить ситуацию когда ордер пришёл раньше сделки.
Это конечно сложная задача, но вполне реализуемая. На каждый у меня уходит времени от 3 недель до шести.
Начать можно с TWIME, ЛУА(используйте Quik Sharp), Транзака или OEC. Работы много на самом деле.
Если соберётесь писать и чувствуете в себе силы джедая, напишите мне чтобы не вышло так что несколько человек работает над одним подключением.
Удачных алгоритмов!
Комментарии