Creating a Telegram bot to track your crypto portfolio

In Software Development


In this tutorial we will create a Telegram bot to track your personal Cryptocurrency portfolio on Binance
alt
Editorial Commitee Qualified.One,
Management
alt

In this tutorial we will create a Telegram bot for your personal needs that can do the following:

  • Show current asset size in USD(T).
  • Show changes in the portfolio in USD(T) per week and over time.

It is critical that I don't use any "all exchanges in one place" kind of app as a proxy - yes, I don't want to give my keys to anyone.

If you need one, let's write one.

What will we need?

We will use cryptocurrency exchange Binance as an example in this article. It's worth noting that there are no restrictions on using it with other exchanges - the whole framework is the same. Except that API interaction will be a little bit different (it depends on exchange).

  • We will need the functionality of Telegram (where without it).
  • We'll need Dropbox instead of a database (oh, yes).
  • We will need a tool to run our bot, I personally use Heroku, but you can use AWS.

Creating a bot in Telegram

This step has been described hundreds of times in a huge number of articles, which are easy to find. So here will be brief.

We need to find the @BotFather contact in Telegram and run the sequence of commands


/start
/newbot

# We need to enter the new bot's private name and its public name
# I'll use the unpopular public name because that's where our finances will be
# Here we get the API token of our bot and save it
# Remember the link like t.me/alias_binance_bot

Base

First, we'll prepare Dropbox.

We'll follow the link and get our API token for Dropbox by clicking on the Create App button. I'll create access to a separate folder in Dropbox.

Creating a Telegram bot to track your crypto portfolio

Business process transformation

On the current page we will need to generate an OAuth 2.0 for Dropbox:

Creating a Telegram bot to track your crypto portfolio

Once created, let's go to the Permissions tab and set permissions on files.content.write

Now at the link we have the APPS folder. Go into it, then go into the subdirectory with the name of our bot. There we need to put the file totalData.txt, which contains only an empty list.


[]

Interaction with Binance

We need to create our API key on the Binance exchange.

This activity follows this link.

The result of this activity for us will be API Key and Secret Key. In our case, the read-only permission will be enough.

Creating a Telegram bot to track your crypto portfolio

As the next step we will write the code which we will use further on.


import json
import time
import logging
import os
import binance
import dropbox
from binance.client import Client

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    level=logging.INFO)

logger = logging.getLogger(__name__)

binanceKey = ['BinanceKey']
binanceSecret = ['BinanceSecret']

dropboxToken = 'DropboxKey'

SLEEP_TIMEOUT = 1 * 60

def getBalance(idx):
    client = Client(binanceKey[idx], binanceSecret[idx])
    balances = client.get_account()['balances']
    balanceUsd = 0
    prices = client.get_all_tickers()
    for b in balances:
        curB = float(b['free']) + float(b['locked'])
        asset = b['asset']
        if curB < 0.000001:
            continue
        if asset == "USDT":
            balanceUsd += curB
        prices = sorted(prices, key=lambda p: p['symbol'])
        for p in prices:
            if p['symbol'] == str(asset) + "USDT":
                balanceUsd += float(curB) * float(p['price'])
    balanceUsd = float("%.2f" % balanceUsd)
    curt = time.time()
    return balanceUsd, curt

def getAccountInfo():
        amountUsd, timenow = getBalance(0)
        return {'usd': amountUsd, 'ts': timenow}

def loadJsonFromDropbox(dbx):
    for i in range(1):
        try:
            meta, resp = dbx.files_download('/totalData.txt')
            print(meta, resp)
            body = resp.content.decode('utf-8')
            resp.close()
            return json.loads(body)
        except Exception as ex:
            time.sleep(0.5 * (2 ** i))
            print(ex)

def saveJsonToDropbox(dbx, content):
    jsonBytes = json.dumps(content, indent=4).encode('utf-8')
    dbx.files_upload(jsonBytes, '/totalData.txt', mode=dropbox.files.WriteMode('overwrite'))

def addInfoPointToDropbox(dbx):
        content = loadJsonFromDropbox(dbx)
        content += [getAccountInfo()]
        saveJsonToDropbox(dbx, content)

def main():
    dbx = dropbox.Dropbox(dropboxToken)
    while True:
        addInfoPointToDropbox(dbx)
        time.sleep(SLEEP_TIMEOUT)
    amountUsd, timenow = getBalance(0)
    print(amountUsd)
    print(timenow)

if __name__ == '__main__':
  main()

First, let's try to run this code locally. If everything is done correctly - the code will be executed every 60 seconds and after some time the file totalData.txt should look like this


[
    {
        "usd": 2.81,
        "ts": 1670699696.930476
    },
    {
        "usd": 2.82,
        "ts": 1670699760.437554
    },
    {
        "usd": 2.84,
        "ts": 1670699823.819883
    },
    {
        "usd": 2.86,
        "ts": 1670700537.611635
    },
    {
        "usd": 2.88,
        "ts": 1670700600.6501918
    }
]

Some more code. How to read diff
As I wrote above, I am using Heroku. In addition, until recently, this service was free, but times are changing and you may wonder whether you should use Heroku or swear by AWS. If you decide to use Heroku, I can recommend this article.

The bot itself will have one key command - stats.

API keys from Binance will not be needed here. Only the token obtained when registering the bot in Telegram and the Dropbox token (remember, Dropbox replaces our database?). To calculate the information for a week, we simply generate a list of balances for the week. If necessary, it's easy to change the code and count diffs for any time slice.


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://your_heroku_app.com/' + TOKEN)

    updater.idle()

if __name__ == '__main__':
    main()

In lieu of a conclusion.
As a result of applying the stats command, you should get e.g. the following message in response:

Creating a Telegram bot to track your crypto portfolio

It is worth noting that this bot can be further developed into even more applications.

For example, you can implement a buy or sell command. You can connect additional accounts or multiple exchanges and monitor all of your portfolios in one place. Convenient? Yes, it is!

To connect additional accounts, just add another call in this place (and of course additional api keys - there they are already in the list).


amountUsd, timenow = getBalance(1)

However, we'll leave these exercises for dear readers to work on their own. But everyone has different needs ? .

In general, all sources are already presented in the article, but just in case - they are also on GitHub.