Блог им. WinterMute
Приветствую! В предыдущем посте была теория, теперь к делу. Кое-что буду упрощать, чтобы представить картинку в целом.
Итак, чтобы проект не зависел от API внешней com библиотеки (SmartCom или д.р.), чтобы в коде стратегий не использовались специфические типы, разработку я начала с обёрток над смарткомом. Я определил три базовых интерфейса: IConnectGate, IMarketDataGate и IPortfolioGate. Соответственно для подключения, для получения маркет-даты и для выставления заявок и работы с портфелем. Причём каждый из этих трёх интерфейсов мне надо было реализовать минимум дважды – для смарткома и для локального тестера.
В случае со смарткомом, это некий адаптер-обёртка, благодаря которому, я оперирую собственными типами и не завишу от com библиотеки. Т.е. у меня есть свои типы (например, направление заявки, тайм-фрейм), которые используются в коде, а адаптер-обёртка конвертирует их в специфические, понятные внешней библиотеке. Также, желательно, чтобы у каждого объекта, в программе, была только одна обязанность, поэтому никакой дополнительной логики эти обёртки не несут.
В случае с локальным тестером, реализации – это по большей части mock объекты, заглушки. Например, реализация IConnectGate для смарткома, это вызов метода подключения и подписка на результат этого вызова, а в случае с локальным тестером мы никуда физически не подключаемся, а сразу генерируем событие “ Ok! Мы подключились!”.
Далее, я определил интерфейс IStrategy, всего лишь с одним методом – Start. Вообще, изначально планировал сделать несколько классов – класс генерирующий сигналы, класс, отвечающий за риск менеджмент, класс, который ведёт и закрывает позицию… Но, подумав, решил не усложнять – особых плюсов это не дало бы, а только запутало. Все стратегии, которые я бы не делал, все реализуют интерфейс IStrategy. В конструктор стратеги передаются необходимые настройки (торгуемый тиккер, размер допустимой просадки, коэффициенты), инициализируются переменные и происходит подписка на события получения маркет-даты. А метод Start просто запускает процедуру получения маркет-даты и стратегия начинает работать. Конечно, можно было бы определить ещё несколько методов, благодаря которым, мы бы как-то влияли на стратегию во время её работы, или получали от неё какие-то данные, но пока опустим эти детали.
Если стратегий несколько, и они используют специфические настройки, лучше использовать фабрику для их создания. Фабрика стратегий по некоему ID стратегии возвращает конкретную её реализацию, тем самым мы делегируем фабрике функцию создания стратегий:
interface IStrategyFactory { IStrategy CreateStrategy(int strategyID); }
Согласно шаблону MVP, один презентер отвечает за одно представление (форму), каждое представление должно иметь презентера. Я создал два класса – некий MainPresenter и MainView для него. При старте программы, после необходимых настроек, вызывается конструктор MainPresenter’а и далее открывается MainView. В последующих постах, я буду детальнее описывать и развивать эту часть. Хочу подчеркнуть – внешний вид не важен, информацию можно получать и из консоли. Учитывая всё вышесказанное можно описать MainPresenter’а в первоначальном виде:
class MainPresenter { private IMainView mainView; private IStrategyFactory strategyFactory; private IConnectGate connectGate; public MainPresenter(IMainView mainView, IStrategyFactory strategyFactory, IConnectGate connectGate) { this.mainView = mainView; this.strategyFactory = strategyFactory; this.connectGate = connectGate;<br /> this.connectGate.Connected += () => this.strategyFactory.CreateStrategy(1).Start(); this.connectGate.Connect(); } public IMainView View => mainView; }
В теле конструктора MainPresenter’а происходит инициализация переменных, подписка на событие подключения и собственно подключение. Почём подписка на событие могла бы выглядеть так:
private void ConnectHandler() { ... } connectGate.Connected += ConnectHandler;
Но первый способ более лаконичен, и для небольших блоков более приемлем. Это т.н. лямбда выражения, или анонимные функции. Лямбды упрощают код, также лямбды используются в выражениях linq (это я покажу в дальнейшем).
Вот и всё. Мы скрыли все детали реализации, нам не важно, что за брокер, какова будет реализация интерфейсов, какая у нас стратегия торговли, мы не зависим от специфических библиотек – всё это решается в другом месте. Мы обобщили подход – инициализировали объекты, подписались на событие подключения, подключились, и запустились. Причём на данном этапе, все реализации могут быть заглушками.
P.S.
С днём знаний, друзья! ;-)