Pull to refresh

Как сделать костыль для Тинькофф Инвестиций своими руками или уведомления об action required for take profit / stop loss

Reading time 4 min
Views 14K
Есть такой брокер — Тинькофф Банк. И есть проблема в том, что на текущий момент этот брокер не имеет приказов take profit / stop loss. Поэтому, если вы хотите торговать более активно, то вам нужно костылить какое-то временное решение, пока в недрах Тинькоффа программисты разрабатывают киллер фичу take profit/stop loss, и под катом — одно из них.
update: 22.03.2019, Брокер выкатил мажорную версию 3.0.0 в Google Play, в которой take profit/stop loss все-таки появились.

Почему я решил написать эту статью здесь? Мне показалось, что Тинькофф Банк и его продукты довольно популярны среди айтишников, и, возможно, у кого-то есть такая же потребность, а желания или времени городить свой велосипед нет. Поэтому делюсь своим.

Для начала — об альтернативных возможностях, предоставляемых собственно Брокером.
Первое, у Тинькоффа есть лимитные заявки, которые появились в феврале 2019 (два года ждали, без шуток!), но они работают в пределах одного дня и что хуже — в небольшом денежном интервале, что на волатильном рынке создает неудобства. Просто нельзя задать значения меньше (больше) определенного порога, рассчитываемого от текущих котировок. Ну и задать больше одной лимитной заявки, вероятно, нельзя (у меня при попытке сохранить первую заявку мобильное приложение всегда крашится, а на сайте такой функциональности нет).
Второе, внутри их мобильного приложения можно подписаться на изменение цены, установив абсолютный порог или порог на изменение в процентах (на увеличение или уменьшение), но вы можете установить один и только один порог на актив.

Логика моего велосипеда проста:
1) у нас есть thresholds (здесь и далее — пороги) для нашей ценной бумаги (актива), на который у нас должно происходить ручное действие take profit / stop loss. Пороги рассчитываем самостоятельно, исходя из цены покупки актива;
2) мы должны парсить откуда-то данные текущей цены актива;
3) посылать себе извещение, если один из порогов был достигнут.
Несмотря на незамысловатое описание, есть нюансы в реализации :)

1) Пока мой портфель состоял из одного актива, пороги прописал прямо в теле скрипта, а поиск бумаги сделан был даже не переменной, а просто магической строчкой. Плохое решение, но для проверки идеи было достаточно. По мере диверсификации портфеля я сделал файл, откуда грузятся ценные бумаги, биржи, на которых они торгуются и значения порогов.

2) Поскольку моим активом была иностранная ценная бумага, которая торгуется на Санкт-Петербургской бирже, то сначала я решил парсить данные с сайта Санкт-Петербургской биржи, со следующей страницы: spbexchange.ru/ru/market-data/Default.aspx
Сортировка на СПб бирже идёт по объему торгов, и моя ценная бумага всегда находилась по первой странице. Работало замечательно, но 8 марта всё сломалось. Почему-то TSLA оказалась аж на 25-ой странице, а их пагинатор грузит данные динамически через JS. Такую проблему можно решить «в лоб»: парсить все страницы, до тех пор, пока не найдем наш актив. Но такой подход не очень эффективен, если считать время выполнения цикла скрипта. Вместо этого я решил добавить парсинг с tradingview.com. Там не нужно лопатить длинные списки на большем количестве страниц. Там у каждого актива есть примерно такая ссылка:
www.tradingview.com/symbols/NASDAQ-TSLA
Мне казалось, что всё должно завестись быстро и просто, но возникла проблема — интересующие меня данные подгружаются через JS и обычный Requests с этим не справился.
У этой проблемы есть три известных мне решения:
PyQT, selenium (webdriver) и расширение Requests-HTML. Поскольку у меня в проекте уже был Requests, было решено использовать его же расширение.
К сожалению, работало это решение не очень стабильно, пришлось поискать варианты решения.
    session = HTMLSession()
    r = session.get(url)
    my = r.html.render(timeout=30)
    selector = 'span.tv-symbol-header-quote__value.tv-symbol-header-quote__value--large.js-symbol-last'
    price = r.html.find(selector)[0].text
    r.close()
    session.close()

Обратите внимание на timeout, а также вызовы метода close(). Их не во всех примерах можно встретить, но с ними работает лучше, чем без них.

3) Регистрируемся на сервисе, который умеет посылать СМСки (sms.ru), берем их API, создаем ключ. До 5 СМСок в сутки — бесплатно. Мне — достаточно.
Ключ выглядит так:
24A41EA5-EEEE-CCCC-5555-094143C2EDDD
а отправка СМС в первых версиях была реализована вот так:
def send_message(mymessage):
    sms_url = 'https://sms.ru/sms/send?api_id=key&to=number&msg=message&json=1'
    sms_url = sms_url.replace('key', mykey)
    sms_url = sms_url.replace('number', mynumber)
    sms_url = sms_url.replace('message', mymessage)
    sms_response = requests.get(sms_url)


Во время разработки возник следующей вопрос: а что делать, если мы уже послали пользователю СМС о пересечении порога? Пока проверок никаких не было, оно посылало СМС еще раз. Каждый раз. Довольно быстро «съел» бесплатный лимит и стал думать, что с этим делать. Пришлось добавить счетчик отосланных СМС (sms_counter), который мы проверяем перед вызовом send_message.

    global sms_counter
    sms_counter = sms_counter + 1


Прицепом пойдет еще один вопрос: отлично, во время торговой сессии мы обрабатываем одно пересечение порога определенным активом, и нас это устраивает. Что делать к следующей торговой сессии? Было решено обнулять счетчик высланных СМС. Вариантов было три: хранить данные в БД (но у меня, на текущей момент, stateless приложение), парсить время/дату или перезапускать скрипт. Пока что я делаю третий вариант, но в перспективе перейду ко второму или к первому варианту.

Сейчас решение уже работоспособно, и его можно скачать с Гитхаба
Для пользователей, которые не понимают, что такое Python и как его настраивать, предлагаю попробовать запустить упакованное решение для Windows

Планы для дальнейшего развития:
1) парсить дату/время, для обнуления счетчика СМС (вместо перезапуска скрипта);
2) сейчас это stateless приложение, но намереваюсь привинтить БД;
3) после п.2, хочу добавить отслеживание резких скачков увеличение/уменьшение цены, относительно цены закрытия предыдущего дня;
4) расширить «коммуникационные» возможности: больше путей (Telegram, Viber, голосовые звонки, другие варианты) и провайдеров (намерен добавить smsc.ru, так как sms.ru иногда теряет отзывчивость, и, хоть и посылает СМС, но скрипт не выполняется дальше до тех пор, пока мы не получим sms_response).
Only registered users can participate in poll. Log in, please.
Пользуетесь ли вы Тинькофф Инвестициями?
50.38% Да 66
49.62% Нет 65
131 users voted. 19 users abstained.
Only registered users can participate in poll. Log in, please.
Если вы пользуетесь Тинькофф Инвестициями, была ли полезна для вас эта статья?
45.16% Да 28
54.84% Нет 34
62 users voted. 44 users abstained.
Tags:
Hubs:
+8
Comments 11
Comments Comments 11

Articles