Мне был необходим для личного удобства бот в Телеграм, который умеет выполнять следующие действия:
Показать текущий размер активов в USD(T).
Показать изменения по портфелю в USD(T) за неделю и за все время.
Мне критически важно не использовать никакое приложение вида «все биржи в одном месте» в качестве proxy — да, я не хочу никому предоставлять свои ключи.
Если нужен — давайте напишем.
Что нам потребуется?
В качестве примера в данной статье я буду использовать криптовалютную биржу Binance. Стоит отметить, что никаких ограничений на использование с другими биржами нет — весь каркас тот же самый. Разве что взаимодействие по API будет немного другим (зависит от биржи).
Нам потребуется функционал Telegram (куда же без него).
Нам потребуется Dropbox вместо базы данных (о, да).
Нам потребуется инструмент, где будет крутиться наш бот. Лично я использую Heroku, но можно использовать и AWS.
Создаем бота в Телеграм
Данный этап был описан сотни раз в огромном числе статей, найти которые не составляет проблем. Поэтому здесь будет кратко.
Нам нужно найти в телеграмме контакт @BotFather и выполнить последовательность команд
/start
/newbot
# Нужно ввести приватное имя нового бота и его публичное имя
# Я использую непопулярное публичное имя, т.к. тут будут наши финансы
# Здесь же мы получим API token нашего бота, сохраним его
# Запомним ссылку вида t.me/alias_binance_bot
База из топора
Для начала мы подготовим Dropbox.
Перейдем по ссылке и получим свой API token для Dropbox, нажав на кнопку Create App. Я создам доступ на отдельную папку в Dropbox.
На текущей странице нам необходимо будет сгенерировать OAuth 2.0 для Dropbox:
После создания пройдем на вкладку Permissions и установим права на files.content.write
Теперь по ссылке у нас появилась папка APPS. Зайдем в нее, далее зайдем в поддерикторию с названием нашего бота. Туда нам необходимо поместить файл totalData.txt, содержащий только пустой список.
[]
Взаимодействие с Binance
Нам необходимо создать наш API ключ на бирже Binance.
Для начала попробуем запустить данный код локально. Если все сделано правильно — код будет исполняться каждые 60 секунд и спустя некоторое время файл totalData.txt должен выглядеть как-то так:
Далее я приведу пример кода, с помощью которого мы будем получать сами изменения портфеля, а так же этот же код должен запускаться ботом.Как я уже писал выше — я использую Heroku. К тому же до недавнего времени данный сервис был бесплатным, но времена меняются и уже можно задуматься — стоит ли использовать Heroku или присягнуть AWS. Если вы, как и решите выбрать Heroku — я могу посоветовать данную статью.
Сам бот будет иметь одну ключевую команду — stats.
API ключей от Binance здесь уже не потребуется. Только token полученный при регистрации бота в Telegram и Dropbox token (вы же помните, Dropbox заменяет нам базу данных?). Для подсчета информации по неделе мы просто генерим список по балансам за неделю. При необходимости код несложно изменить и считать diff за любой временной срез.
import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import os
import dropbox
import time
import json
import datetime
PORT = int(os.environ.get('PORT', 5000))
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
TOKEN = os.environ.get('TELEGRAM_TOKEN', None)
dropboxToken = ''
def start(update, context):
update.message.reply_text('Hi!')
def loadJsonFromDropbox(dbx):
meta, resp = dbx.files_download('/totalData.txt')
body = resp.content.decode('utf-8')
resp.close()
return json.loads(body)
def getHistory():
dbx = dropbox.Dropbox(dropboxToken)
prices = loadJsonFromDropbox(dbx)
timeNow = time.time()
dtNow = datetime.datetime.now()
dtToday = dtNow.replace(hour=0, minute=0, second=1)
dtWeek = dtToday - datetime.timedelta(days=dtToday.weekday())
dtAllTime = dtNow - datetime.timedelta(days=100000)
stats = {
'this week': {
'since': dtWeek.timestamp(),
'till': dtNow.timestamp(),
'prices': []
},
'all time': {
'since': dtAllTime.timestamp(),
'till': dtNow.timestamp(),
'prices': []
}
}
for item in prices:
for stat in stats:
if stats[stat]['since'] < item['ts'] < stats[stat]['till']:
stats[stat]['prices'].append(item)
text = ''
totalBalance = 0.
totalBalanceUsd = 0.
for stat in stats:
usdt = [p['usd'] for p in stats[stat]['prices']]
if len(usdt) >= 1:
u1 = usdt[-1]
u2 = usdt[0]
valueUsd = '{:+.2f} USD'.format(u1 - u2)
else:
values = 'n/a'
text += '{}: {}n'.format(stat, valueUsd)
if stat == 'all time':
totalBalanceUsd = u1
dt = datetime.datetime.fromtimestamp(prices[-1]['ts'])
text += 'nLast update: {:%A %H:%M:%S} UTC+0n'.format(dt)
return update.message.reply_text(text, parse_mode='markdown')
def main():
updater = Updater(TOKEN, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("stats", getHistory))
dp.add_handler(MessageHandler(Filters.text, echo))
dp.add_error_handler(error)
updater.start_webhook(listen="0.0.0.0", port=int(PORT), url_path=TOKEN)
print(TOKEN + ' <- TOKEN | ' + str(PORT) + ' <- PORT')
updater.bot.setWebhook('https://ваш_хероку_апп.com/' + TOKEN)
updater.idle()
if __name__ == '__main__':
main()
Вместо заключения
В результате применения команды stats вы должны получить в ответ, например, такое сообщение:
Стоит отметить, что данного бота можно развивать и дальше до еще более прикладных применений.
Например, можно реализовать команду buy или sell. Можно подключить дополнительные аккаунты или сразу несколько бирж и мониторить все свои портфели в одном месте. Удобно? Удобно!
Для подключения дополнительных аккаунтов достаточно добавить еще один вызов в этом месте (и конечно же дополнительные api ключи — там они уже и так в list’е).
amountUsd, timenow = getBalance(1)
Однако данные упражнения мы оставим дорогим читателям для самостоятельной работы. Всё же потребности у всех разные ?
В целом, все исходники уже представлены в статье, но на всякий случай — также они на GitHub.
Telegram изменил политику конфиденциальности, позволяя передавать IP-адреса и номера телефонов пользователей по запросу властей без судебного постановления