Сделаем выгрузку открытых позиций по опционам из терминала SmartX от IT Invest на сайт по анализу опционов option.ru. Будем использовать язык Python и пакет pandas.
Для начала посмотрим на файл который генерирует option.ru. Похоже на JSON, но это не он - валидацию он не пройдёт и его нельзя импортировать. Скорее это извлечённый из списка JSON. Методом тыка было установлено что поля updatePeriod или description не являются обязательными.
{
name: 'Somename RTS 2020',
filter: 'RTS',
description: 'Создан 26.04.23',
portfolio:
[
{
type: 'futures',
open: 100190,
code: 'RIM3',
qty: '1'
},
{
type: 'option',
open: 40150,
code: 'RI60000BD3D',
qty: '1'
},
]
},
{
name: 'Somename Si 1010',
filter: 'Si',
description: 'Создан 26.04.23',
portfolio:
[
{
type: 'option',
open: 14951,
code: 'Si65500BD3D',
qty: '1'
},
{
type: 'futures',
open: 80452,
code: 'SiM3',
qty: '-1'
}
]
},
{ updatePeriod : 300000}
В терминале SmartX в окне открытых позиций нет поля которое позволяет однозначно установить какой тип имеет инструмент: облигация, акция, АДР, ETF, опцион на фьючерс, опцион на акцию или ещё что.
Такое поле есть в окне котировок, но для этого нужно каждый инструмент туда добавлять, потом выгружать сразу и таблицу котировок, и таблицу открытых позиция, а потом как-то их сопоставлять SQL-подобным методом Join.
Задача немного упрощается если торговля ведётся не на счёте с единой позицией с суффиксом MO, а на одной секции срочного рынка на счёте с суффиксом RF, ведь там торгуются только фьючерсы и опционы на них, которые нам и нужны.
Более того, на MO счёте с единой позицией на основных серверах нельзя торговать опционами. Поддержка ссылается на требования Центрального Банка и говорит что гарантийное обеспечение на отдельном RF счёте для сугубо срочного рынка якобы меньше. Наверное, там какие-то требования риск-менеджмента или баги с софтом...
Тип инструмента: опционы и фьючерсы
Если производить выгрузку только один раз, выгружая открытые позиции без котировок, то определить тип инструмента можно по полям "Инструмент" и "Тикер". Можно реализовать некую логику чтобы определение было по обоим полям на тот случай, если одно из полей выгружено не было т.к. было отключено в окне.
Инструмент Si-6.23_FT Si78500BD3 Si78500BD3B
Тикер SiM3 Si78500BD3 Si78500BD3B
В поле "Инструмент" понять фьючерс ли это можно по постфиксу "_FT", а всё остальное будет опционами.
df.loc[df['code'].astype(str).str.endswith('_FT'), 'type'] = 'futures'
По полю "Тикер" нужно придумать более сложное регулярное выражение:
df.loc[df['ticker'].astype(str).str.match(r'(?!([a-zA-Z]+)[0-9]+[a-zA-Z]{2,4}[0-9]+)'), 'type'] = 'futures'
Ещё есть поле "Крат. Наименование", по которому можно легко определить опцион это или фьючерс. Поле имеет подстроку "CA" для коллов, "PA" для путов и ничего из оного для фьючерсов. К сожалению, значение в поле не совпадает с тем которое понимает option.ru:
Крат. Наименование
Si-6.23 Si-6.23M200423CA78500 Si-6.23M130423PA78500
Количество инструментов
С первого взгляда всё просто, но оказывается количество может быть в штуках и лотах. Это можно выбрать в настройках терминала. Соответственно каждый раз будут разные поля:
К-во(лоты)
К-во(штуки)
Также есть поля со знаком:
К-во со знаком(лоты)
К-во со знаком(штуки)
Нам нужно именно поля со знаком. Если его в выгрузке нет, но можно взять поле количества без знака и посмотреть на поле "Направление". Просто добавляем минус если это шортовая позиция.
df.loc[df['direction'] == 'Long', 'qty'] = df['qty_unsigned']
df.loc[df['direction'] == 'Short', 'qty'] = -df['qty_unsigned']
Базовый актив
Также необходимо определять базовый актив инструмента. Он подставляется в поле "filter" в файле для option.ru. Они определяются немного по разному для полей "Инструмент" и "Тикер".
df['base'] = df['code'].str.extract(r'(?P<base>[a-zA-Z]*)')
df['base'] = df['ticker'].str.extract(r'([a-zA-Z]{2})')
Следует обратить внимание что для некоторых инструментов существует короткое и длинное имя. Option.ru требует длинное имя, поэтому необходимо заменить короткое на длинное:
short_and_long_prefixes = {'RI': 'RTS'}
df['base'].replace(to_replace=short_and_long_prefixes, inplace=True)
Далее собираем всё это вместе под формат option.ru. Просто засунуть в JSON не получится. Сначала делаем списки уникальных портфелей и уникальных базовых инструментов:
portfolios = df['portfolio'].unique()
bases = df['base'].unique()
Получаем строки для каждого портфеля и базового актива:
rows = df[(df['portfolio'] == portfolio) & (df['base'] == base)]
Далее проходимся по ним в циклах и собираем портфель:
row_out = {
'type': row['type'],
'open': str(row['open']),
'code': row['code'],
'qty': str(row['qty']),
}
rows_out.append(row_out)
Накидываем сверху верхушку с именем портфеля, описанием и фильтром по базовому активу и готово.
Аналогично, можно сделать выгрузку и из QUIK. К сожалению, брокер не позволяет иметь вместе сразу два терминала SmartX и QUIK, поэтому проверить не могу.