В окно параметров OsEngine можно выводить не только параметры, но и другие элементы, включая таблицы и чарты.
Данный пример робота служит демонстрацией реализации кастомной таблицы в окне параметров.
В нем показано:
- Динамическая таблица: Таблица обновляется в реальном времени по мере поступления новых данных.
- Взаимодействие с пользователем: Пользователь может изменять данные в таблице и получать значения конкретных ячеек.
- Настраиваемые параметры: Возможность включать и отключать робота и также настройка трейлинг-стопа для выхода.
1. Как это выглядит.
Заходим в тестер и запускаем нашего робота, открываем окно параметров.
Называется: CustomTableInTheParamWindowSample.
1. Вкладка настроек «Base settings»:
В ней находятся настройки:
1. Regime — включение режима торговли.
2. TrailingValue – коэффициент для расчета трейлинг-стопа.
3. MaxPositions – максимальное количество открытых позиций.
4. Volume type – режим выбора объёма.
- Contracts – кол-во контрактов инструмента.
- Contract currency – валюта контракта.
- Deposit percent – процент от депозита.
5. Volume – значение объёма. Что именно, зависит от предыдущего пункта. В случае Contracts тут указывается объём инструмента. В случае Contract currency здесь указывается кол-во рублей или долларов, которыми нужно войти. В случае с Deposit percent здесь указывается % от общего депозита, которым нужно войти в контракт.
6. Asset in portfolio – тут нужно указывать название валюты, которое будет использовано для расчёта объёма, если Вы выбрали тип объёма “Deposit percent”. В тестере оставляем «Prime». На крипте это обычно “USDT”.
2. Вкладка настроек «Table settings»:
Здесь у нас находится сама таблица:
- Security – столбец с названием бумаг.
- Count candle – глубина данных, на которую проверяем движение (Movement to enter).
- Movement to enter – этот столбец мы тоже сами заполняем, он нам потребуется в торговой логике. Если Current movement будет больше Movement to enter, мы заходим в позицию.
- Current movement – текущий процент движения цены бумаги.
- Side – выбор метода входа для каждой бумаги.
2. Где найти робота в проекте?
Ссылка на робота GitHub: https://github.com/AlexWan/OsEngine
3. Разбор робота.
Строки 1-11:
Импортируются необходимые пространства имен:
Строки 23-26:
Определяется пространство имен OsEngine.Robots.TechSamples для организации кода и класс CustomTableInTheParamWindowSample наследует от BotPanel:
Строки31-39:
Переопределение методов GetNameStrategyType и ShowIndividualSettingsDialog.
Первый метод возвращает имя стратегии, второй — пустой, предназначен для отображения индивидуальных настроек.
Строка 44-49:
Создание параметров робота:
Строки 51-52:
Инициализируется панель бота:
Cоздается одна вкладка скринера и сохраняется ссылка на вкладку в _tab для дальнейшего использования.
Строки 56-64:
Создание вкладки и самой таблицы для нее:
Создание вкладки и настройка ее размеров.
Строка 68:
Вызов метода LoadLines:
Строки 72-74:
Подписка на события:
Событие завершения свечи, удаление робота и обновление значений таблицы.
Строки 83-96:
Этот блок отвечает за набор переменных робота:
Строки 101-118:
Метод передает изменения, внесенные пользователем в логику робота:
Метод CellValueChanget:
1. Итерация по строкам DataGridView:
- Проходит по каждой строке в DataGridView.
2. Обновление свойств объектов Lines:
- Извлекает значения из ячеек текущей строки и присваивает их соответствующим свойствам объектов в коллекции Lines.
- Security: Строковое значение из первой ячейки.
- CandleCount: Целочисленное значение из второй ячейки.
- MovementToEnter: Десятичное число из третьей ячейки.
- Side: Перечисление типа Side, полученное из пятой ячейки.
3. Сохранение изменений:
- Вызывает метод SaveLines() для сохранения обновленных данных.
4. Обработка исключений:
- Оборачивает весь код в блок try-catch для перехвата возможных исключений.
- При возникновении исключения записывает его сообщение в лог с помощью метода _tab.SendNewLogMessage().
Строки 123-129:
Данный метод вызывается при удалении робота:
Метод DeleteBotEvent:
1. Проверка существования файла:
- Метод File.Exists проверяет, существует ли файл по указанному пути.
2. Удаление файла:
- Если файл существует, метод File.Delete удаляет его.
Строки 134-152:
Сохраняем данные из таблицы в .txt файл:
Метод SaveLines:
1. Создание потока записи:
- Создает новый поток записи (StreamWriter) в указанном файле. Файл создается или перезаписывается в зависимости от второго аргумента конструктора (false).
2. Итерация по строкам Lines.
3. Запись данных в файл:
- Каждая строка записывается в файл, разделяя ячейки знаком “%”.
4. Закрытие потока записи:
5. Обработка исключений:
- Оборачивает весь код в блок try-catch для перехвата возможных исключений.
- При возникновении исключения записывает его сообщение в лог с помощью метода _tab.SendNewLogMessage().
Строки 157-187:
Загружаем сохраненные данные в таблицу:
Метод LoadLines:
1. Проверка существования файла:
- Проверяет, существует ли файл с указанным именем.
2. Загрузка данных из файла:
- Если файл существует, открывает его для чтения.
- Читает каждую строку из файла и создает новый объект TableBotLine.
- Добавляет созданный объект в коллекцию Lines.
- Закрывает поток чтения.
3. Обновление DataGridView:
- Добавляет новые строки в DataGridView на основе данных из коллекции Lines.
Строки 193-199:
Событие завершения свечи:
Метод _tab_CandleFinishedEvent:
1. _tab_CandleFinishedEvent(List<Candle> candles, BotTabSimple tab):
- Этот метод вызывается при завершении каждой свечи.
- Аргумент tab представляет собой объект, который содержит информацию об этой новой вкладке.
- Вызываются все методы, находящиеся внутри.
Строки 204-261:
Метод, отвечающий за создание столбцов в таблице:
Метод CreateColumnsTable:
1. Проверка потока:
- MainWindow.GetDispatcher.CheckAccess(): Проверяет, вызывается ли метод из UI-потока.
- Если метод вызывается не из UI-потока, то он рекурсивно вызывает себя в контексте UI-потока, чтобы обеспечить безопасное обновление пользовательского интерфейса.
2. Создание контейнера для DataGrid:
- Создается объект WindowsFormsHost, который позволяет разместить элемент управления Windows Forms (DataGrid) в приложении WPF.
3. Создание DataGrid:
- Создается объект DataGrid, и настраиваются его основные свойства:
- Режим выбора строк: Выбирается режим, при котором можно выбрать всю строку целиком.
- Автоматическое изменение высоты строк: Строки будут автоматически подстраиваться по высоте содержимого.
- Выравнивание текста: Текст в ячейках выравнивается по центру как по горизонтали, так и по вертикали.
4. Создание столбцов:
- Создаются пять столбцов с названиями «Security», «Count candle», «Movement to enter», «Сurrent movement» и «Side».
- Для каждого столбца задается тип ячейки (DataGridViewTextBoxCell), ширина столбца (автоматическое заполнение) и заголовок.
5. Добавление столбцов в DataGrid:
- Созданные столбцы добавляются в коллекцию столбцов DataGrid.
6. Размещение DataGrid в контейнере:
- Объект DataGrid устанавливается в качестве дочернего элемента объекта WindowsFormsHost.
7. Обработка ошибок:
- В блоке try-catch обрабатываются возможные исключения. В случае ошибки выводится сообщение в лог.
Строки 266-302:
Метод, который добавляет новые строки в таблицу:
Метод AddNewLineInTable:
1. Проверка наличия инструмента:
- Если для вкладки не задан финансовый инструмент (tab.Security == null), метод прерывает свою работу.
2. Поиск существующей строки:
- Происходит итерация по всем строкам таблицы.
- Если значение в первом столбце строки (название инструмента) совпадает с названием инструмента текущей вкладки, то считается, что строка для этого инструмента уже существует.
3. Создание новой строки:
- Если строка для инструмента не найдена, создается новый объект TableBotLine с необходимыми значениями (название инструмента, количество свечей, движение для входа и т.д.).
- Этот объект добавляется в коллекцию строк.
- Вызывается метод CreateRowsTable для обновления отображения таблицы.
Строки 307-325:
Методы для обновления отображения таблицы:
Метод CreateRowsTable:
1. Проверка потока:
- _tableDataGrid.InvokeRequired: Проверяет, вызывается ли метод из UI-потока.
- Если метод вызывается не из UI-потока, то он рекурсивно вызывает себя в контексте UI-потока, чтобы обеспечить безопасное обновление пользовательского интерфейса.
2. Добавление строки:
- Вызывает метод CreateLine для создания новой строки данных и добавляет ее в таблицу.
3. Сохранение изменений:
- Вызывает метод SaveLines для сохранения изменений в таблице.
4. Обработка ошибок:
- В блоке try-catch обрабатываются возможные исключения. В случае ошибки выводится сообщение в лог.
Строки 327-364:
Этот метод создает линию и возвращает ее вызывающему методу:
Метод CreateLine:
1. Создание строки:
- Создается новый объект DataGridViewRow — строка для таблицы.
2. Создание ячеек:
- Для каждого поля объекта TableBotLine создается соответствующая ячейка таблицы (DataGridViewTextBoxCell или DataGridViewComboBoxCell).
- Ячейки добавляются в коллекцию ячеек строки (row.Cells.Add).
- Для некоторых ячеек устанавливается свойство ReadOnly = true, что делает их содержимое неизменяемым пользователем.
3. Заполнение ячеек данными:
- В блоке try-catch происходит заполнение ячеек значениями из объекта Lines[index]:
- Ячейка «Security» заполняется названием инструмента.
- Ячейка «CandleCount» заполняется количеством свечей для расчета текущего движения.
- Ячейка «MovementToEnter» заполняется значением движения для входа.
- Ячейка «CurrentMovement» заполняется значением текущего движения с добавлением символа процента "%".
- Ячейка «Side» заполняется текстовым представлением стороны сделки («Buy» или «Sell») на основе значения поля Side объекта TableBotLine.
4. Обработка ошибок:
- В случае возникновения исключения при работе с данными выводится сообщение в лог.
5. Возврат строки:
- Заполненная строка таблицы возвращается вызывающему методу.
Строки 369-420:
Этот метод сортирует линии:
\
Метод SortLine:
1. Проверка потока:
- _tableDataGrid.InvokeRequired: Проверяет, вызывается ли метод из UI-потока.
- Если метод вызван не из UI-потока, то он рекурсивно вызывает себя в контексте UI-потока для безопасного обновления интерфейса.
2. Проверка последней строки:
- Проверяет, содержит ли последняя строка таблицы какое-либо значение в первом столбце (_tableDataGrid.Rows[_tableDataGrid.Rows.Count — 1].Cells[0].Value.ToString()).
- Если значение отсутствует, метод завершается, не выполняя дальнейшую обработку.
3. Итерация по строкам:
- Внешний цикл: Перебирает все строки таблицы (_tableDataGrid.Rows.Count).
4. Поиск инструмента:
- Внутренний цикл: Перебирает все вкладки приложения (_tab.Tabs.Count).
- Для каждой строки проверяет, совпадает ли название инструмента в первом столбце (_tableDataGrid.Rows[i].Cells[0].Value.ToString()) с названием инструмента какой-либо вкладки.
- Если совпадение найдено, флаг GotLine устанавливается в true, что означает, что строка найдена.
5. Удаление неиспользуемых строк:
- После прохода по всем строкам, если флаг GotLine остался false (ни для одной строки не найдено совпадение со вкладкой), значит, эта строка не нужна.
- Индекс ненужной строки сохраняется в переменной indexLine.
- Строка с этим индексом удаляется из таблицы с помощью метода _tableDataGrid.Rows.Remove.
- Вызывается метод SaveLines для сохранения изменений.
6. Обработка ошибок:
- В блоке try-catch обрабатываются возможные исключения при работе с данными. В случае ошибки выводится сообщение в лог.
Строки 425-479:
Метод обновления текущего процента движения:
Метод MovementPercentUpdate:
1. Проверка потока:
- _tableDataGrid.InvokeRequired: Проверяет, вызывается ли метод из UI-потока.
- Если метод вызван не из UI-потока, то он рекурсивно вызывает себя в контексте UI-потока для безопасного обновления интерфейса.
2. Поиск строки таблицы:
- Ищет строку в таблице, соответствующую инструменту из вкладки (tab.Security.Name).
- Перебирает все строки коллекции Lines и сравнивает значения поля Security с названием инструмента.
- Если совпадение найдено, то сохраняется индекс строки (indexLine) и флаг GotLine устанавливается в true.
3. Проверка количества свечей:
- Сравнивает количество свечей в полученном списке свечей (candles.Count) с количеством свечей, указанным в найденной строке таблицы (TableCandlesCount).
- Если количество свечей в списке меньше, чем в таблице, метод завершает работу, так как для расчета недостаточно данных.
4. Обновление значения процента изменения цены:
- Если строка найдена (GotLine == true), выполняется расчет процента изменения цены в зависимости от направления сделки (Lines[indexLine].Side):
- Для сделок на покупку (Buy): рассчитывается изменение цены как разница между ценой закрытия предпоследней свечи (candles[candles.Count — Lines[indexLine].CandelCount].Close) и ценой закрытия последней свечи (candles[candles.Count — 1].Close), деленная на цену закрытия последней свечи и умноженная на 100.
- Для сделок на продажу (Sell): расчет аналогичен, но разница цен берется с обратным знаком.
- Результат расчета округляется до двух знаков после запятой (Math.Round) и сохраняется в поле CurrentMovement объекта Lines[indexLine].
- Значение процента изменения цены с добавлением символа "%" устанавливается в соответствующую ячейку таблицы (_tableDataGrid.Rows[indexLine].Cells[3].Value).
5. Сохранение изменений:
- Вызывается метод SaveLines для сохранения изменений в таблице данных.
6. Обработка ошибок:
- В блоке try-catch обрабатываются возможные исключения при работе с данными. В случае ошибки выводится сообщение в лог.
Строки 488-565:
Торговая логика:
Метод TradeLogicMethod:
1. Проверка активности режима:
- Если значение равно «Off», то метод завершается, не выполняя дальнейшие действия.
2. Проверка наличия свечей:
- Если список пустой, метод завершается, так как для анализа недостаточно данных.
3. Поиск строки таблицы:
- Перебирает все строки коллекции Lines и сравнивает значения поля Security с названием инструмента.
- Если совпадение найдено, то сохраняется индекс строки (indexLine), и флаг GotLine устанавливается в true.
4. Проверка наличия строки:
- Если строка в таблице не найдена (GotLine == false), метод завершается.
5. Получение открытых позиций:
- Из вкладки (tab) извлекается список открытых позиций (positions.Count).
6. Логика открытия позиции (если нет открытых позиций):
- Если открытых позиций нет (positions.Count == 0), выполняется проверка условия для открытия позиции:
- Проверяем, не превышает ли общее количество позиций максимального значение, если превышает, то выходим.
- Значение движения для входа в позицию (movementToEnter) извлекается из соответствующей ячейки таблицы (_tableDataGrid.Rows[indexLine].Cells[2].Value.ToString()) и преобразуется в десятичное число.
- Сравнивается текущее движение цены (Lines[indexLine].CurrentMovement) с заданным движением для входа.
- Если текущее движение превышает заданное значение (movementToEnter < Lines[indexLine].CurrentMovement), в зависимости от направления сделки выполняется покупка по рынку с объемом лота, который рассчитывает метод GetVolume, и продажа по рынку с объемом лота, который рассчитывает метод GetVolume.
7. Логика обновления стоп-лосса (если есть открытые позиции):
- Если позиция не открыта (PositionStateType.Open), метод завершается.
- В зависимости от направления открытой позиции (positions[0].Direction):
- Для позиции на покупку (Buy) рассчитывается цена стоп-лосса как разница между минимальной ценой последней свечи и произведением этой цены и значения трейлинга, деленным на 100.
- Для позиции на продажу (Sell) рассчитывается цена стоп-лосса как сумма максимальной цены последней свечи и произведения этой цены и значения трейлинга, деленным на 100.
- Вызывается метод CloseAtTrailingStop вкладки для обновления стоп-лосса открытой позиции (positions[0]) по рассчитанной цене.
Строки 567-656:
Метод GetVolume:
1. Инициализация:
- Создается переменная volume со значением 0 для хранения результата.
2. Проверка типа объема:
- Контракты: Если тип объема установлен как «Contracts», то в качестве объема используется значение из переменной Volume.
- Контрактная валюта:
- Если тип объема установлен как «Contract currency», то объем рассчитывается делением значения Volume на цену последнего спроса.
- Корректировка для OS Trader: Если приложение запущено в режиме OS Trader, то объем дополнительно корректируется с учетом лота инструмента и настроек сервера.
- Округление: Результат округляется до числа десятичных знаков, указанного в настройках инструмента.
- Процент от депозита:
- Если тип объема установлен как «Deposit percent», то:
- Определяется текущая стоимость портфеля в основной валюте.
- Рассчитывается сумма денег, которую необходимо вложить в сделку, исходя из указанного процента от портфеля.
- Объем рассчитывается делением этой суммы на цену последнего спроса и лот инструмента.
- Результат округляется.
3. Возврат результата:
- Возвращается вычисленное значение объема.
Строки 661-696:
Отдельный класс для хранения и передачи данных о строках таблицы:
Свойства:
- Security: Строка, содержащая название финансового инструмента.
- CandelCount: Целое число, указывающее количество свечей, связанных с этим инструментом.
- MovementToEnter: Десятичное число, представляющее значение движения цены, необходимое для входа в позицию.
- CurrentMovement: Десятичное число, представляющее текущее движение цены.
- Side: Перечисление (enum), определяющее сторону сделки (покупка или продажа).
Метод GetSaveStr:
- Инициализация строки: Создается пустая строка saveStr, которая будет использоваться для сборки конечного результата.
- Конкатенация значений:
- К строке saveStr последовательно добавляются значения переменных Security, CandleCount, MovementToEnter и CurrentMovement, разделенных символом процента (%).
- Затем добавляется значение переменной Side без дополнительного разделителя.
- Возврат результата: Готовая строка возвращается как результат работы метода.
Метод SetFromStr:
- Разбиение строки: Строка разбивается на массив строк по знаку “%”.
- Заполнение свойств:
- Security: Первое значение массива присваивается свойству Security.
- CandelCount: Второе значение преобразуется в целое число и присваивается свойству CandleCount.
- MovementToEnter: Третье значение преобразуется в десятичное число и присваивается свойству MovementToEnter.
- CurrentMovement: Четвертое значение очищается от последнего символа (предполагается, что это символ процента) и преобразуется в десятичное число, присваивается свойству CurrentMovement.
- Side: Пятое значение используется для попытки преобразования в перечисление Side. Если преобразование успешно, значение присваивается свойству Side.
Вывод: этот пример робота будет полезен для демонстрации, как создавать и настраивать кастомные элементы в окне параметров робота. Он показывает, как создавать таблицу для вывода значений и использование ее пользователями, что может служить готовым шаблоном для реализации подобных задач.
Удачных алгоритмов!
Комментарии открыты для друзей!
OsEngine: https://github.com/AlexWan/OsEngine
Поддержка OsEngine: https://t.me/osengine_official_support
Регистрируйся в АЛОР и получай бонусы: https://www.alorbroker.ru/open
Сайт АЛОР БРОКЕР: https://www.alorbroker.ru
Раздел «Для клиентов»: https://www.alorbroker.ru/openinfo/for-clients
Программа лояльности от АЛОР БРОКЕР и OsEngine: https://smart-lab.ru/company/os_engine/blog/972745.php