Published on

Réaliser un bot télégram de finance pour les néophytes

 12 mins
Authors
  • avatar
    Name
    Léo Delpon
    Twitter

Faire un bot télégram pour la finance

Depuis que je suis en 4ème, j'ai toujours été bercé par le monde de la finance et de l'entreprenariat. Malheureusement le carry c’était mon père. ll a géré pour moi mon argent jusqu’à ce que je puisse être assez mature pour le gérer tout seul. Maintenant j’ai 25 ans, je vais bientôt être diplômé de mon école, cela fait maintenant 3 ans que je suis en freelance et je ne sais toujours pas gérer mon argent...

J’ai donc commencé par rattraper tout le retard que j’avais eu en demandant à la source (le bon vieux daron). C’est là que j'ai commencé à créer des petits outils pour monitorer mon argent car étant un peu tête en l’air, j’ai parfois du mal à m’informer sur les actualités économiques et financières. Voilà pour j’écris ce petit article, c’est pour vous montrer que coder des outils simples c’est très rapide à faire et ça peut parfois aider un peu ! (petite pensée à mon ami Lectra, sniff)

Dans cet article je vais vous apprendre à coder un bot qui permettra de retracer le cours de bourse de vos actions facilement. Nous calculerons le taux de croissance :

  • Par rapport à la veille
  • Par rapport à une semaine
  • Par rapport à un mois
  • Par rapport à six mois
  • Par rapport à un an

Bon j'avoue, je ne me suis pas ultra foulé mais c'est pour vous montrer qu'on peut faire des choses rapidement !

Trouver une API qui soit assez performante pour voir à moyen termes

Après quelques recherches, je suis tombé sur une API qui avait l’air très intéressante, celle de Yahoo Finance. Si vous êtes novice dans le domaine de la finance et que vous ne souhaitez pas consacrer de nombreuses heures à l'étude de documents techniques, l'API Yahoo Finance représente un choix judicieux.

Non seulement elle est entièrement gratuite (quoique des offres payantes sont disponibles pour les utilisateurs exigeants dans d’autres plateformes), mais elle offre également un large éventail de données sur les marchés, dont certaines ne sont pas disponibles sur d'autres plateformes. De plus, l'installation de l'API est extrêmement aisée et rapide. Vous pouvez ainsi obtenir des clés API personnelles en quelques clics et même automatiser les tâches les plus répétitives grâce aux fonctions pré-construites dans les modules.

L'API Yahoo Finance représente une véritable porte d'entrée pour les débutants dans le monde de la finance, leur permettant de prendre leurs premiers pas en toute sérénité. Pour tout les plus curieux, je vous invite à lire cet article !

Il est temps de coder le module simple en python

On commence d’abord par importer les modules dont on a besoin :

from datetime import datetime
import yfinance as yf

Vous pouvez remarquer que cette librairie yfinance permet d’utiliser l’API de Yahoo Finance. Néanmoins, il faut savoir que ce dernier n’est pas maintenu par Yahoo personnellement mais est un module open-source. Je vous recommande d’aller lire les termes d’utilisations pour un usage plus particulier.

Si vous ne possédez pas cette librairie, je vous propose de l’installer

Untitled
Untitled

A présent, commençons par tester cette dernière :

element = yf.Ticker("CS.PA")
print(element.info)
print(element.history(period="1y"))

Si on analyse le résultat ci-dessous, on remarque deux choses, les informations fournies sont assez complètes mais surtout en analysant l’historique, on peut apercevoir l’intervalle de temps de l’historique qui est entre hier et il y a un an. Cette période peut évoluer en indiquant une période différente. (voir la documentation de yfinance 🙃)

$ py finance.py
{'address1': '25, avenue Matignon', 'city': 'Paris', 'zip': '75008', 'country': 'France', 'phone': '33 1 40 75 57 00', 'website': 'https://www.axa.com', 'industry': 'Insurance—Diversified', 'sector': 'Financial Services', 'longBusinessSummary': "AXA SA, through its subsidiaries, provides insurance, asset management, and banking services worldwide. The company operates through six segments: France, Europe, Asia, AXA XL, International, and Transversal & Central Holdings. It offers life and savings insurance products, such as savings and retirement, other health, and personal protection products. The company also provides property and casualty insurance products, including car, home, and personal or professional liability to individual and business clients; international insurance for large corporate clients in Europe; and marine and aviation insurance services, as well as property and casualty reinsurance products. In addition, it offers asset management services in the areas of various asset classes, including equities, bonds, hedge funds, private equity, and real estate for the group's insurance companies and their clients, and retail and institutional clients. Further, the company provides health, term life, whole life, universal life, endowment, and other investment-based products for personal/individual and commercial/ group customers. AXA SA was founded in 1852 and is headquartered in Paris, France.", 'fullTimeEmployees': 92695, 'companyOfficers': [{'maxAge': 1, 'name': 'Dr. Thomas  Buberl', 'age': 49, 'title': 'CEO & Director', 'yearBorn': 1973, 'fiscalYear': 2022, 'totalPay': 2871631, 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Ms. Helen  Browne', 'age': 60, 'title': 'Group Gen. Counsel & Employee Representative Director', 'yearBorn': 1962, 'fiscalYear': 2022, 'totalPay': 93794, 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Mr. Alban  de Mailly Nesle', 'age': 52, 'title': 'Group CFO and Chief Risk & Investment Officer', 'yearBorn': 1970, 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Dr. Alexander  Vollert', 'age': 53, 'title': 'Group Chief Operating Officer', 'yearBorn': 1969, 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Gregoire de Montchalin', 'title': 'Chief Accounting & Reporting Officer', 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Mr. Nicolas  Leclercq', 'title': 'Head of Group Corp. Fin. & Treasury', 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Mr. Marc  Blottière', 'title': 'Group Chief Information Officer', 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Ms. Anu  Venkataraman', 'title': 'Head of Investor Relations', 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Mr. Andrew  Wallace-Barnett', 'title': 'Group Chief Compliance Officer', 'exercisedValue': 0, 'unexercisedValue': 0}, {'maxAge': 1, 'name': 'Ms. Ulrike  Decoene', 'title': 'Group Chief Communication, Brand & Sustainability Officer', 'exercisedValue': 0, 'unexercisedValue': 0}], 'auditRisk': 5, 'boardRisk': 8, 'compensationRisk': 5, 'shareHolderRightsRisk': 1, 'overallRisk': 4, 'governanceEpochDate': 1680307200, 'compensationAsOfEpochDate': 1672444800, 'maxAge': 86400, 'priceHint': 2, 'previousClose': 29.455, 'open': 29.64, 'dayLow': 29.095, 'dayHigh': 29.68, 'regularMarketPreviousClose': 29.455, 'regularMarketOpen': 29.64, 'regularMarketDayLow': 29.095, 'regularMarketDayHigh': 29.68, 'dividendRate': 1.7, 'dividendYield': 0.0581, 'exDividendDate': 1683504000, 'payoutRatio': 0.5442, 'beta': 1.337786, 'trailingPE': 10.333334, 'forwardPE': 8.068493, 'volume': 916383, 'regularMarketVolume': 916383, 'averageVolume': 5203177, 'averageVolume10days': 3967380, 'averageDailyVolume10Day': 3967380, 'bid': 0.0, 'ask': 0.0, 'bidSize': 0, 'askSize': 0, 'marketCap': 66690703360, 'fiftyTwoWeekLow': 20.335, 'fiftyTwoWeekHigh': 30.34, 'priceToSalesTrailing12Months': 0.6431119, 'fiftyDayAverage': 28.4945, 'twoHundredDayAverage': 26.24995, 'trailingAnnualDividendRate': 1.7, 'trailingAnnualDividendYield': 0.05771516, 'currency': 'EUR', 'enterpriseValue': 113347788800, 'profitMargins': 0.06437, 'floatShares': 1819891043, 'sharesOutstanding': 2264539904, 'heldPercentInsiders': 0.20195, 'heldPercentInstitutions': 0.42022, 'impliedSharesOutstanding': 0, 'bookValue': 20.028, 'priceToBook': 1.4704415, 'lastFiscalYearEnd': 1672444800, 'nextFiscalYearEnd': 1703980800, 'mostRecentQuarter': 1672444800, 'earningsQuarterlyGrowth': -0.222, 'netIncomeToCommon': 6493000192, 'trailingEps': 2.85, 'forwardEps': 3.65, 'pegRatio': 1.04, 'lastSplitFactor': '4:1', 'lastSplitDate': 989971200, 'enterpriseToRevenue': 1.093, 'enterpriseToEbitda': 11.104, '52WeekChange': 0.1569128, 'SandP52WeekChange': 0.0008276701, 'lastDividendValue': 1.7, 'lastDividendDate': 1683504000, 'exchange': 'PAR', 'quoteType': 'EQUITY', 'symbol': 'CS.PA', 'underlyingSymbol': 'CS.PA', 'shortName': 'AXA', 'longName': 'AXA SA', 'firstTradeDateEpochUtc': 651481200, 'timeZoneFullName': 'Europe/Paris', 'timeZoneShortName': 'CEST', 'uuid': '8d6b474a-15eb-38c4-9e9e-2bff261dcf4c', 'messageBoardId': 'finmb_121238', 'gmtOffSetMilliseconds': 7200000, 'currentPrice': 29.45, 'targetHighPrice': 36.0, 'targetLowPrice': 28.0, 'targetMeanPrice': 33.1, 'targetMedianPrice': 33.3, 'recommendationMean': 1.8, 'recommendationKey': 'buy', 'numberOfAnalystOpinions': 19, 'totalCash': 26168999936, 'totalCashPerShare': 11.556, 'ebitda': 10208000000, 'totalDebt': 63393001472, 'quickRatio': 0.27, 'currentRatio': 0.595, 'totalRevenue': 103699996672, 'debtToEquity': 115.733, 'revenuePerShare': 45.284, 'returnOnAssets': 0.007929999, 'returnOnEquity': 0.099530004, 'grossProfits': 19490000000, 'freeCashflow': 2330125056, 'operatingCashflow': 7880999936, 'earningsGrowth': -0.186, 'revenueGrowth': -0.066, 'grossMargins': 0.18794, 'ebitdaMargins': 0.09844, 'operatingMargins': 0.090050004, 'financialCurrency': 'EUR', 'trailingPegRatio': None}
                                Open       High        Low      Close   Volume  Dividends  Stock Splits
Date
2022-04-28 00:00:00+02:00  24.396057  24.597639  23.875695  24.086653  5465441        0.0           0.0
2022-04-29 00:00:00+02:00  24.199162  24.283546  23.791312  23.871006  5931332        0.0           0.0
2022-05-02 00:00:00+02:00  23.645984  23.716304  23.350645  23.613169  5229984        0.0           0.0
2022-05-03 00:00:00+02:00  23.660049  23.936638  23.556914  23.899134  5564054        0.0           0.0
2022-05-04 00:00:00+02:00  23.978829  24.025708  23.552227  23.589729  4929845        0.0           0.0
...                              ...        ...        ...        ...      ...        ...           ...
2023-04-24 00:00:00+02:00  29.200001  29.400000  29.150000  29.320000  3202571        0.0           0.0
2023-04-25 00:00:00+02:00  29.200001  29.309999  28.924999  29.285000  3450686        0.0           0.0
2023-04-26 00:00:00+02:00  29.240000  29.410000  28.875000  29.270000  3886950        0.0           0.0
2023-04-27 00:00:00+02:00  29.315001  29.610001  29.135000  29.455000  2878200        0.0           0.0
2023-04-28 00:00:00+02:00  29.639999  29.680000  29.094999  29.450001   916383        0.0           0.0

[259 rows x 7 columns]

On note quelque chose d’intéressant, c’est que la disposition de l’historique est sous forme de DataFrame, nous allons donc pouvoir faire une selection assez simplement (normal, c'est le but de python d'être simple). Créons une fonction pour obtenir tout les cours de bourses de aujourd’hui à partir de différents symboles que l'on appelle aussi Ticker:

# On utilise les symboles boursiers pour récupérer les informations
pea_stock_options = [
    "CS.PA",
    "BNP.PA",
    "LSS.PA",
    "MC.PA",
    "PAEJ.PA",
    "PASI.PA",
    "EWLD.PA",
    "PUST.PA",
    "PSP5.PA",
    "PWG.PA",
    "TTE.PA"
]

def get_current_stock_value():
    result = []
    print("[+] Getting stocks values")
    now = datetime.now()
    t_string = now.strftime("%d/%m/%Y %H:%M:%S")

    for symbol in pea_stock_options:
        element = yf.Ticker(symbol)
        hist = element.history(period="1y")
        result.append(f"[{t_string}] {hist.iloc[-1]['Close']}")

    message = "\n".join(result)
    del result[:]
    print(message)
    return message

Et voici le résultat :

delpo@LAPTOP-M0MPIPJ6 MINGW64 ~/Documents/tools/test_scrapper
$ py finance.py
[+] Getting stocks values
[28/04/2023 15:22:43] 29.469999313354492
[28/04/2023 15:22:43] 58.09000015258789
[28/04/2023 15:22:43] 28.950000762939453
[28/04/2023 15:22:43] 867.9000244140625
[28/04/2023 15:22:43] 15.680000305175781
[28/04/2023 15:22:43] 9.08899974822998
[28/04/2023 15:22:43] 24.00200080871582
[28/04/2023 15:22:43] 47.70000076293945
[28/04/2023 15:22:43] 31.8799991607666
[28/04/2023 15:22:43] 2.2100000381469727
[28/04/2023 15:22:43] 57.040000915527344

Plutôt cool n’est-ce pas ? Bien, à présent, passons aux choses sérieuses ! On va créer les différents taux de croissances. Commençons par créer la fonction qui va calculer la croissance

def evolution_rate(value_start, value_end):
    return ((value_end - value_start) / value_start)*100

Maintenant, passons au calculs pour toutes nos métriques temporelles

def rate_overall(hist):
    rates = dict()
    rates["last_annual_rate"] = round(evolution_rate(float(hist.iloc[0]['Close']), float(hist.iloc[-1]['Close'])), 2)
    rates["last_six_month_rate"] = round(evolution_rate(float(hist.iloc[-132]['Close']), float(hist.iloc[-1]['Close'])), 2)
    rates["last_month_rate"] = round(evolution_rate(float(hist.iloc[-22]['Close']), float(hist.iloc[-1]['Close'])), 2)
    rates["last_week_rate"] = round(evolution_rate(float(hist.iloc[-5]['Close']), float(hist.iloc[-1]['Close'])), 2)
    rates["last_rate"] = round(evolution_rate(float(hist.iloc[-2]['Close']), float(hist.iloc[-1]['Close'])), 2)
    return rates

On a donc nos deux petits outils, voici la version finale de get_current_stock_value():

def get_current_stock_value():
    result = []
    print("[+] Getting stocks values")
    now = datetime.now()
    t_string = now.strftime("%d/%m/%Y %H:%M:%S")

    for symbol in pea_stock_options:
        element = yf.Ticker(symbol)
        hist = element.history(period="1y")
        rates = rate_overall(hist)
        result.append(f"[{t_string}] {element.info['longName']} ({element.info['symbol']}) : \n{round(float(hist.iloc[-1]['Close']), 2)} € [1A:{rates['last_annual_rate']} ] [6M:{rates['last_six_month_rate']} %] [1M:{rates['last_month_rate']} %] ([1W:{rates['last_week_rate']} %] [Y:{rates['last_rate']} %]")
    message = "\n\n".join(result)
    del result[:]
    print(message)
    return message

et voici le resultat :

$ py finance.py
[+] Getting stocks values
[28/04/2023 15:31:41] AXA SA (CS.PA) :
29.48[1A:22.37 ] [6M:18.68 %] [1M:8.5 %] ([1W:0.53 %] [Y:0.07 %]

[28/04/2023 15:31:41] BNP Paribas SA (BNP.PA) :
58.08[1A:26.04 ] [6M:23.67 %] [1M:11.76 %] ([1W:-1.79 %] [Y:-1.12 %]

[28/04/2023 15:31:41] Lectra SA (LSS.PA) :
29.25[1A:-26.37 ] [6M:-2.82 %] [1M:-16.55 %] ([1W:-14.97 %] [Y:-11.36 %]

[28/04/2023 15:31:41] Lyxor PEA Nasdaq-100 UCITS ETF (PUST.PA) :
47.7[1A:-4.12 ] [6M:4.57 %] [1M:3.09 %] ([1W:2.11 %] [Y:0.89 %]

[28/04/2023 15:31:41] Lyxor PEA S&P 500 UCITS ETF (PSP5.PA) :
31.87[1A:-5.41 ] [6M:-1.2 %] [1M:2.33 %] ([1W:0.21 %] [Y:0.73 %]

[28/04/2023 15:31:41] Prodways Group SA (PWG.PA) :
2.21[1A:-15.65 ] [6M:-39.45 %] [1M:-5.56 %] ([1W:-2.21 %] [Y:-1.12 %]

[28/04/2023 15:31:41] TotalEnergies SE (TTE.PA) :
57.14[1A:30.87 ] [6M:11.34 %] [1M:6.78 %] ([1W:-1.84 %] [Y:0.47 %]

Ca commence à avoir un peu plus de panache ! On peut passer à la partie du bot télégram.

Réalisation du bot et envoi des données à un interval de temps régulier

Pour créer un bot, je ne vais pas réinventer la roue, je vous propose d’aller plutôt voir mon article sur comment faire un bot télégram. On va donc utiliser notre fichier .env afin d’être capable d’importer notre clé d’API de télégram.

import telebot
import os.path
from dotenv import load_dotenv

load_dotenv()

bot = telebot.TeleBot(os.getenv('TELEGRAM_API_KEY_STOCK'))

On crée ensuite notre fonction d’envoi de message de télégram

@bot.message_handler(commands=['start'])
def get_cup_data(message):
    text = "Hi, welcome back to stock bot register !"
    bot.send_message(message.chat.id, text, parse_mode="Markdown")
    while True:
        response = get_current_stock_value()
        bot.send_message(message.chat.id, response)
        time.sleep(120)

bot.infinity_polling()

Si nous lançons notre bot :

Untitled

Et voici les résultats à partir de notre liste de symbole boursier. J’ai mis le tout dans une boucle infinie avec une intervalle de 120 secondes. Nous recevrons donc nos informations toute les deux minutes. (bah ouais, faut pas non plus SPAM comme un en**ulé)

Voilà, j’espère que cela vous aura plu car j’ai pris beaucoup de plaisir à coder ce petit bot. Le prochain article qui aura comme thématique la finance sera dans pas trop longtemps et je parlerai d’une plateforme qui est très intéressante qui est : TradingView. Je vous apprendrai à coder une dashboard totalement modulable de finance.

Allez PEACE mes chers nakamas.