Найти в Дзене
Квант

Выгрузка открытых позиций из SmartX в Option.ru

Сделаем выгрузку открытых позиций по опционам из терминала 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 : 30000
Оглавление

Сделаем выгрузку открытых позиций по опционам из терминала 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, опцион на фьючерс, опцион на акцию или ещё что.

Открытые позиции в терминале SmartX
Открытые позиции в терминале SmartX

Такое поле есть в окне котировок, но для этого нужно каждый инструмент туда добавлять, потом выгружать сразу и таблицу котировок, и таблицу открытых позиция, а потом как-то их сопоставлять SQL-подобным методом Join.

Котировки в терминале SmartX
Котировки в терминале SmartX

Задача немного упрощается если торговля ведётся не на счёте с единой позицией с суффиксом 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, поэтому проверить не могу.