Часть 1. Starting up
Первым делом неплохо было бы разобраться с коннекторами.
Уже есть Альфадирект и Квик. Мне бы хотелось подрубить свой кастомный для IB. Но незадача: В каркасе старая версия S#. Это грозит проблемами в работе с гидрой и коннекторами. У меня возникли сразу, потому что коннектор был на 4.1.19.1.
Значит заодно и обновлюсь.
Удаляю все, что связано со stocksharp и Ecng из reference, и добавляю заново все свежее. У меня вот такой список получился.

После обновления, ничего не заработает.
Поправил в ConnectionEngine для квика и для альфа аналогично.

После ребилда все должно взлететь.
Добавил, естественно, нужные Refercences, и создание коннектора по аналогии.


Настройки. Тут есть целый менеджер настроек, который отвечает за сохранение, и т.д.
Добавил в список коннекторов и нужные настройки в SettingsProperties


Ну вот и все.


Comentarios (27)
Iniciar sesión o Crear cuenta, Inicie sesión o regístrese para dejar un comentario
Часть 2. Почва для удобного добавления стратегий.
В прошлый раз я немного поторопился с обновлением версии S#. Нашлись кое-какие неполадки в коннекторе и пришлось откатиться. Тем не менее, там все актуально за исключением парочки нюансов при создании новых подключений. Главное делать по аналогии, и все будет хорошо👍
Добавил базовые свойства стратегии.
и базовую стратегию, восстанавливающую ордера,сделки и позиции при перезапуске.
Правильней было бы сохранять все в бд, используя EntityRegistry, но как-то с базой гидры не хотело взлетать. А так работает, ну и окей. Не так уж часто стратегии перезапускаются в самом деле.
Немного поменял добавление стратегий. Теперь, когда нужно будет включить новую стратегию, можно просто сделать для нее команду добавления, приписать в xml и она появится в менюшке добавления. Главное - все по аналогии.👍
Часть 3.1. Добавление своих стратегий.
Эта история полна нюансов.
В шелле хорошо показано, на примере одной стратегии как все работает и т.д.
Добавить свою взамен существующей тоже легко - меняем да и все. Либо по-варварски вставляем в существующую новые внутренности.
Но тут не так все гибко, чтобы наряду с существующими в два щелчка добавить новые стратегии и чтобы все прекрасно работало и сохранялось.
Можно чуть менее чем полностью переделать SettingsEngine, половину кода связанного с добавлением стратегий, и только потом добавлять какие угодно новые стратегии в два щелчка.
А можно сделать минимум изменений, прикрутив маленькие костыли и затем добавлять новые стратегии не в 2 а в 6 щелчков, о чем и пойдет речь дальше.
Я уже подготовил небольшую почву, наподобие той, про которую было во 2-ой части. Когда мне удастся подружиться с ТФС, эти изменения уже будут в шелле.
Может показаться, что кода много, но на самом деле там 90% копи паст.
Добавляем заготовку для стратегии, для параметров и параметров тестирования, которые когда-нибудь наполним содержанием.
Идем в SettingsEngine, добавляем методы для сохранения и загрузки параметров для нового типа стратегий.
Идем в Main.
Добавляем маленький костыль.
И почти как под копирку методы для добавления стратегий:
Нужно добавить в InitializeConfiguration() - методе восстановления из настроек.
Ну вот. Теперь сохранение настроек стратегий будет работать не правильно, а удаление будет удалять только из интерфейса, а при перезапуске все будет по-прежнему. И это только на первый взгляд.
Как починить это, а также о том, как добавить кнопки добавления стратегии в интерфейс, узнаем в следующей подчасти части 3.
Прям триллер какой-то! Ждем продолжения! 👍
Переделать метод сохранения, который в MainWindow.xaml.cs можно как-нибудь в таком духе:
Сохранять сначала наши новые кастомные стратегии, а потом фильтровать все, например по имени. Но по идее, если уж пилить стратегии, то для каждого вашего типа стратегий будет свой массив, который и следует сохранять по отдельности, и ничего уже потом не фильтровать.
Аналогично с сохранением свойств тестовых стратегий:
В методе ```csharp private void ExecutedRemoveStrategy(object sender, ExecutedRoutedEventArgs e)
Удаляем еще и из нашего списка стратегий с фильтрацией по имени. Лучше, конечно, что б у всех стратегий был какой-то id, и все сравнения проводить по нему.
Что бы можно было было из интерфейса добавить свежих стратегий:
Добавляем команду там же в MainWindow.xaml.cs,
далее методы для команды.
Нужно теперь подключить команду к gui. В MainWindow.xaml:
Там есть такая штука, комманд биндинг. Добавляем вот что:
далее в нем же ищем "AddStrategyCommand"? находим его в контексте меню итемс, и аналогично добавляем
Ну вот, теперь все готово.
Небольшое лирическое отступление...
Там выше в стратегии реализовывались методы для сохранения ордеров, позиций и т.д.
У меня так раньше было реализовано, не на s# shell, но все же. Возможно версия s# была другая, возможно я где-то наошибался, но недавно на s# shell уже обнаружил, что такой способ не работает. Т.е. если положить Dictionary с ордерами (то что в базовых Properties) в SettingsStorage и попытаться сериализовать, ничего не получится. Если положить SettingsStorage с ордерами в SettingsStorage, тоже не заработало, хотя раньше все прокатывало, если мне не изменяет память.
Во-вторых это, на мой взгляд, не совсем корректно, если в стратегию ордера и сделки поступают не только из Trader, но еще и напрямую из хранилища.
Может возникнуть какая-нибудь дурацкая ситуация, если после перезапуска один и тот же ордер прийдет в стратегию и шлюза и из хранилища, и бог его знает как-там оно все разбираться внутри будет.
Поэтому, в Properties стратегии лучше хранить просто список orderIDs, брать их от Trader'a и привязывать.
Если торговый терминал не передает историю ордеров, прийдется "усовершенствовать" Trader и реализовать возможность сохранения в бд.
Вот такие вот рекомендации.🙂
Вопрос немного не в тему. Удалось ли Вам поймать устойчивую конфигурацию работы с Plaza 2, т.е. версию, которая с плазой пашет?
Я плазой вообще дел не имел
По просьбам телезрителей(как минимум одного) сделал зарисовочку для сопровождения позиции. Открывается прямо при старте, и выходит в какое-то время, а также по стопам и нескольким ценовым тейкам, передвигая стоп в б\у. Есть кое-какие лишние проверки, потому что кусочки взяты из стратегии на нескольких инструментах, но хуже не будет.
Интересно, это только у меня в каркасе в таблице сделки, отображается только первая сделка? Никакие последующие в нее не заносятся... Или это общий баг такой?
да у меня тоже был такой баг, почему-то insert не так работает, как хотелось бы.
В StrategyDocument.xaml.cs меняем закомментированное на незакомментированное.
где взять исходники S#.Shell, тем у кого лицензия это позволяет? Дайте, пожалуйста, ссылку.
скачал версию 4.2.2, там вообще Shell не открывается, библиотек основных нет.
Месяц прошел - и молчание разработчиков!)) На самом деле Shell действительно достаточно сырой продукт, в который необходимо много вложить чтобы получить выхлоп. Даже в версии от Казакова Сергея достаточно ошибок и неточностей, на исправление которых у меня ушло множество времени.
Так почти месяц назад я поставил для себя цель провести миграцию своих торговых роботов с платформы TradeMatic на платформу S#.Shell, и за это время наработал материал, которым стоит поделиться дабы не возникали подобные вопросы.
Часть 4. Универсализация создания, тестирования и исполнения стратегий
Первое что мне бросилось в глаза в версии от Kazai Mazai, которую я продолжил допиливать - отсутствие универсальности в исполнении/тестировании одной и той же стратегии. Так если запустить стратегию RobotStrategy на тестирование, то следующий участок кода стратегии:
вызовет ошибку "Не удалось привести тип объекта "System.Collections.Generic.List
1[Robot.Strategies.HustleEveryDayStrategyTestingProperties]" к типу "System.Collections.Generic.List1[Robot.Strategies.HustleEveryDayStrategyProperties]".Для решения этой проблемы введем в класс BaseShellStrategyProperties два параметра "Класс стратегии" и "Режим запуска":
Данные свойства необходимо определять во всех процедурах типа AddХХХХStrategy.
4.1. Универсализация исполнения стратегий Все параметры внутри стратегии обертываем процедурой if, которая проверяет режим запуска и проводит правильное приведение типов данных:
4.2. Универсализация тестирования стратегий В основную процедуру запуска стратегий на тестирование StartTesingStrategy добавляем проверка класса стратегии при задании специфичных параметров:
4.3. Универсализация сохранения настроек стратегий В основной процедуре сохранения настроек стратегии SaveStrategies проводим модификацию запроса Where
4.4. Устранение бага отображения стратегий на панели "Стратегии" В поставляемой версии на панели "Стратегии" не отражаются тестовые стратегии. Для устранения этого бага проводим универсализацию хранения стратегий в переменной _documents. а) везде в процедурах создания стратегий проводим сохранение стратегий в переменную _documents:
б) в свойстве SelectedStrategy дописываем условие выбора документа типа TestingDocument:
Теперь все отображается корректно. в) дополнительно можно вообще удалить процедуру SaveTestingStrategies, поместив соответствующие обработки в SaveStrategies:
После всех этих универсализаций добавление новой стратегии действительно становится достаточно простым делом.
Часть 5. Чему нас учит семья и школа
Интерфейс тестирования S#.Shell по умолчанию не достаточен для эффективного тестирования стратегии при ее первичном кодировании, так как в этом тонком процессе переноса собственного нейронного вещества в машинный код возникает множество нюансов а-ля "сделка не выполнилась в силу неизвестных причин", в которых невозможно разобраться без соответствующих инструментов.
Поэтому добавляем в наш S.Shell основные инструменты визуализации процесса исполнения стратегии, которые нам уже знакомы по курсу S#.Education - исторический график свечей, таблицу заявок и таблицу собственных сделок.
5.1. Создаем на TestingPanel элемент типа TabControl, в который добавляем вкладки для каждого инструмента:
5.2. В общем-то мы понимаем, что графические элементы в целом тормозят тестирование и нужны будут не всегда, а в основном при кодинге новых стратегий, поэтому в свойства базовой стратегии тестирования BaseShellStrategy добваляем свойства для управления отображением их в ходе тестирования:
5.3. В процедуре тестирования стратегии StartTesingStrategy прописываем код для обоработки каждого нового инструмента: а) для истории свечей:
б) для отражения истории сделок:
в) для таблицы заявок:
г) для таблицы сделок:
И в результате все работет отлично, кроме корректного отображения сделок))
А почему для установки стопов не пользуетесь StopLossStrategy, а в ручную их отслеживаете?
на мой взгляд использование дочерних стратегий тут дело вкуса - кому-то идет, а кому-то нет; в силу разных обстоятельств - и дебаггить их невозможно, условия срабатывания ограничены (н--р нет стоп-лосса исходя из % годовых) и пр.
Часть 6 - Тотальный апгрейд
Итак имеем S#.Shell на основе S#.API 4.1.15.0 и решаемся на апгрейд до версии 4.2.2.16. Сразу же получаем множество заменой iTrader на Connector, и еще с десяток проблемных мелочей типа замены свойств, которые решаются в течение получаса
Но так же вылезают две более серьезные проблемы, о которых имеет смысл тут рассказать.
где ConnectCommand - исходная RoutedCommand, ExecutedConnect - основная процедура для выполнения, CanExecuteConnect - связанная проверочная процедура возможности выполнения основной процедуры, miConnect - название элемента пункта меню "Подключиться", далее в тоже процедуре InitializeCommands() создаем необходимые элементы для быстрых клавиш:
2)добавляем в код процедуры StartStrategy специальную проверку на режим эмуляции и в случае оного, создаем тестовый портфель, который и привязываем к коннектору:
Вуаля- все работает как нужно!
lebedevsrg, собираетесь ли Вы выкладывать свою версию shell'a куда-нибудь?
methyst, тут такие правила что по окончанию срока обучения (1,5 или 3 мес ) пользователей отключают от TFS. По вопросу - полагаю что смысла каждому пользователю выкладывать свои программы на TFS особого нет. Материалы я тут опубликовал для общего развития тех кто захочет сам допиливать Shell.
Таких правил нет. http://stocksharp.com/posts/m/29944/
Часть 7 - Чем StockSharp круче Tradematic, или о миграции стратегий
Итак рассмотрим вкратце миграцию стратегий на примере простейшей стратегии торговли по линиям капитала, которая подробно изложена тут (http://robotcraft.ru/vstroeniestrategii/strateg-akc). Заодно пополним коллекцию торговых стратегий StockSharp достаточно надежной и низкорискованной стратегий, хотя с небольшой годовой доходностью (9% -15%).
В целом система Tradematic так же позволяет программировать стратегии на с#, но без доступа к исходному коду оболочки, т.е. это что-то типа S#.Studio. При этом в ней есть ряд преимуществ по сравнению со S#.Studio:
Итак для реализации нашей стратегии создаем общий класс TradeManager, назначением которого будет:
создание и ведение Плана торговли(Plan),
ведение Торговой книги(Book). Данный класс сразу делаем общий, так как он в дальнейшем может быть использован в пирамидальных стратегиях любой сложности.
Создаем класс TradeManager и его инициализатор:
Так же создаем класс для быстрой параметризации Плана торговли при его первичном создании:
Далее создаем метод CreateTradePlan для создания План торговли по указанным параметрам:
б) затем создаем процедуру UpdateBook, которая призвана записывать в Торговую книгу данные о связанных сделках покупки и продажи: в зависимости от направления проводим связывание элементов Заявка-Сделка:
В самой стратегии реализуем несколько открытых свойств для связи с интерфейсом:
И далее создаем три основных процедуры, которые будут обеспечивать ее работу: а) процедуру инициации, основное предназначение которой - создать План торгов до старта стратегии
б) процедуру запуска, в которой устанавливаем основные вызовы процедур обработки свечей и ведения Торговой книги
в) и конечно же процедуру обработки свечей, в которой реализуем основную логику создания заявок на покупку/продажу актива:
б) создаем в классе TradeManager счетчик CalcCapReq для подсчета использованного капитала:
в) вызываем данный счетчик после открытия длинной позиции, например в процедуре UpdateBook с) при обработки сигнала на покупки проверяем наличие капитала в процедуре CheckCap
и если капитала недостаточно для удержания уже набранной позиции, то открытие новых позиций не проводим, создавая об этом запись в логе this.AddWarningLog("Trade is missed for capital breach!")
Итак в целом миграция завершена. Нужно признать что кода в StockSharp получилось гораздо больше, чем Tradematic - раза в три больше. Но тут как раз и пора показать основное преимущество StockSharp - открытость кода для создания пользователького интерфейса управления работающей стратегией.
и выводим его на вкладку в элементе TestingPanel стандартного интерфейса S#.Shell +
по умолчанию делаем элемент скрытым, чтобы не мешал при работе других стратегий. б) создаем StrategyBook для визуализации торговой книги
и так же выводим его в TestingPanel со начальным скрытым состоянием:
Как результат - получаем готовый интерфейс для управления работой стратегии в процессе ее выполнения. Это именно то, что никак невозможно сделать в Tradematic в силу закрытости кода.
Часть 8 - Универсализация интерфейсов исполнения стратегий
Сделаем еще один шаг к раз0витию S#.Shell - проведем универсализацию интерфейсов исполнения. Как известно в стандартной версии S#.Shell тестовые стратегии выполняются на панеле (документе), где присутствуют графики и где можно добавить собственные панели (см. Часть 5), а исполнение на живом подключении проводится на панеле с сокращенным набором элементов. Это не всегда удобно. Да и для новых стратегий тоже важно в первые месяцы их жизни следить за их состоянием с использованием графических элементов.
Для решения этой задачи вначале разбираемся как в В S#.Shell создаются панели. Панель это на самом деле объект типа LayoutDocument из библиотеки AvalonDock, в котором можно задавать различный контент путем присвоения свойства Content типа object. При этом в свойство Content записывается пользовательский элемент управления типа UserControl, графические элементы которого и определяют расположение и набор элементов. Исполняемая стратегия у данного элемент управления это всего лишь свойство типа BaseShellStrategy, которое очевидно может выполняться в любом режиме - как тестовом, так и боевом.
С учетом этого для получения возможности запускать стратегии с любым типом панели делаем следующие вещи:
При этом enDocType задаем как перечисляемый тип
Причем по каким-то причинам в случае прямого вызова InitializePanes(doc) при исполнении стратегии происходят ошибки связанные с пересечением потоков, поэтому как в приведенном выше коде использовалась лямбда-функция InitPanes, которая получает тестовый документ через SelectedTestingDocument и вызывввается в потокобезопасном режиме.
И вот все готово!
Часть 9 - Проходим АД AdoNeta, или о сохранении параметров в базе данных
Как известно, в базовой версии S#.Shell отсутствует функционал, позволяющий восстановить позиции после перезапуска робота. В версии от КазайМазай был предложен метод сохранения в xml-файл через набор свойств базовой стратегии (OrdersByTransactionId, SecuritiesByTransactionId, TradesByTransactionId, PortfoliosByTransactionId, ExchangesByTransactionId) типа SettingsStorage. При этом у меня при всех старании активировать данное сохранение, данный метод не заработал - при активации RestorePositionsOnStart записанные теги оказывались пустыми. На форуме так же описан метод сохранения ордеров в txt-файл, который судя по простоте работает.
Но все же сохранение в файл при всей простоте восстановления позиций такого важного элемента в созхранении записей как ведение управленческого учета прибылей и убытков от работы робота.
Именно по этой причине целесообразно реализовать функционал сохранения истории торгов в базу данных (MSSQLSRV, MSACCESS, SQLLITE и пр.) - гибкость в последобработке данной информации позволяет автоматизировать ведение учета результатов торгов.
Рассмотрим реализацию сохранения состояния робота в базу данных основе AdoNet.
а) функция инициализации будет содержать переменную по проверке готовности БД IsVerified, а так же вызов заданных пользователем параметров: curStor - вид базы данных, ShellConfigDB - название каталога MSSQLSRV (файла для MSACCESS и SQLLITE)
б) переменные для хранения универсальных команд создания базовых таблиц (tblOrders - таблица заявок, tblTrades - таблица сделок), добавления данных и удаления данных из этих таблиц
в) создадим ряд оберток для различных функций по работе с базами данных: в данном случае функции возвращают MSSQLSRV- специфичные объекты, и не являются универсальными обертками, но принцип ясен
г) создадим функцию VerifyStor, которая будет вызываться при первичном обращении к классу для проверки готовности БД к приему данных, если БД не готова - отсутствуют таблицы, то автоматом создаются новые структуры:
При этом конструкция if (IsVerified) return; позволяет минимизировать время проверки за счет сохранения статуса "Проверено" в свойстве IsVerified.
а) в свойства добавляем поле StrategyID для сохранения GUID в текстовом виде
б) вносим восстановление GUID в различные функции создания стратегий а-ля AddRobotStrategy/AddRobotTestStrategy в классе MainWindow:
а) функция SaveToDB(Strategy str) принимает на вход стратегию, сделки и заявки которой необходимо сохранить
б) функция LoadFromDB (Strategy str) принимает на вход стратегию, сделки и заявки которой необходимо загрузить ```csharp
а) запиливаем вызов SaveToDB в функцию SaveStrategies, проверяя при этом наличие в параметрах стратегии установленного свойства RestorePositionsOnStart
б) аналогично запаиваем вызов SaveToDB в правила WhenOrderRegistered / WhenNewMyTrades
в) запихиваем вызов LoadFromDB в различные функции создания стратегий а-ля AddRobotStrategy/AddRobotTestStrategy в классе MainWindow:
И вуаля - сохранение в базу MSSQLSRV работает как часы! Выглядит все просто, но чтобы пройти реально этот Ад, мне потребовалось 2 месяца арбайтена и штудирена))
Аналогично можно сохранять любые другие свойства стратегии, например PnL. Хотя тут я пошел другим путем и сделал динамическое восстановление PnL из сохраненных сделок.
Часть 10 - Обеспечение стабильной работы робота
Итак после всех мероприятий робот работает стабильно, торгует и восстанавливает при сбоях позиции - и самое главное - зарабатывает деньги. Но только КОГДА работает!
И тут возникают две дополнительные проблемы с обеспечением стабильности работы.
При этом данные функции нужно делать именно производными от системной библиотеки user32.dll, т.к. использование System.Windows.Forms.Cursor и System.Windows.Input.Cursor не дет нужного эффекта - это все го лишь функции отрисовки изображения курсора, а нам нужны функции эмуляции работы мыши.
И вот каждые 30сек мы получаем маленькое и практически незаметное для пользователя подергивание мышки, которое однако выполняет свою основную функцию - предотвращает погружение ПК в преждевременный сон.
устанавливаем NU-get пакет Hardcodet.NotifyIcon.Wpf, который обеспечивает работы с иконками в системной трее.
создаем пользовательский словарь ресурсов SysTrayResources.xaml, в котором создаем TaskbarIcon - объект иконки с системной трее, и его контекстное меню
Как видно из кода пункты контекстного меню вызывают функции ShowWindowCommand/HideWindowCommand/ExitApplicationCommand, которые расположены в классе SysTrayViewModel.
При этом нужно пояснить что DelegateCommand - это класс-делегат для ускоренного создания выполняемых команд
Итак - контекстное меню прекрасно работает. Но этого мало))
В событии PasswordChangedHandler прописываем проверку заданного нами пароля и если все ок - то отображаем основное окно робота
И после этого в коде класса SysTrayViewModel вместо мгновенного открытия приложения прописываем старт формы UIpassword