Integration von lemon.markets in Deinen Telegram-Bot (Teil 2 von 2)


blog photo
Veröffentlicht von Joanne Snel am 29. September 2021
Insights


Hinweis: Wenn Du den ersten Artikel dieser zweiteiligen Serie noch nicht gelesen hast, klicke hier, um zu erfahren, wie Du Telegram einrichtest und mit der lemon.markets API verbindest.

Hallo! Ich bin Joanne und gehöre zum Team von lemon.markets. Wir sind ein Berliner Start-up, das automatisiertes Trading über APIs ermöglicht. Unser Ziel ist es, Entwicklern und Entwicklerinnen alle Werkzeuge zur Verfügung zu stellen, mit denen sie ihre eigene Trading Experience an der Börse bauen können. Dabei gibt es hunderte Use-Cases für unser Produkt, wie beispielsweise ein eigenes Frontend zum Platzieren von Orders. Dafür kannst Du bei Null anfangen oder einen bereits existierenden Dienst wie beispielsweise Telegram nutzen. In diesem Artikel erweitere ich das Projekt, das wir in unserem letzten Artikel vorgestellt haben. Wir fügen verschiedene states in der Konversation hinzu, die mit den Endpunkten von der lemon.markets API kommunizieren. Außerdem zeige ich Dir, wie Du Text-Input in API-Requests umwandelst und dem*der Benutzer:in nützliche Daten zur Verfügung stellst, z. B. den aktuellen Preis eines Wertpapiers, bevor Du eine Order bestätigst. Beim Lesen dieses Artikels lernst Du, wie Du Deinen Telegram-Bot so programmieren kannst, dass er eine Order platzieren und aktivieren kann.

Ich gehe davon aus, dass Du Deinen Telegram-Bot bereits eingerichtet und mit dem Autorisierungsprozess von lemon.markets verbunden hast. Wenn nicht, solltest Du diesen Artikel zuerst lesen. Wenn Du unseren Bot erst ausprobieren möchtest, bevor Du Deinen Eigenen implementierst, kannst Du @LemonTraderBot direkt in Telegram anschreiben. Wir haben den Bot als Beispiel für Dich eingerichtet, um zu zeigen, wie er aussehen könnte. Lass uns also erst einmal festhalten, wo wir gerade stehen und wohin wir letztendlich wollen:

Aktueller Stand der Dinge 🔎

Im Moment führt der Bot folgende Aktionen aus:

  1. Aufforderung die client-ID einzugeben,
  2. Aufforderung den client secret einzugeben, 
  3. Abrufen des lemon.markets Access Tokens über den Authentifizierungs-Endpunkt. 

Mit diesem Token können wir alle API-Requests authentifizieren, die wir in den folgenden Erweiterungen des Projekts machen werden. Im vorherigen Artikel haben wir einen ConversationHandler eingerichtet und verwendet, um die beiden verschiedenen Konversations-states zu speichern. Jetzt fügen wir einfach zusätzliche states hinzu, die bestimmte API-Requests auf der Grundlage von Texteingaben ausführen.

Was ist der Plan? 🏁

Wir wollen den Bot so strukturieren, dass die folgenden Schritte erfüllt werden können:

  1. Aufforderung an den*die Nutzer:in, eine bestimmt Art Wertpapier auszuwählen,
  2. Aufforderung an den*die Nutzer:in, den Namen des Wertpapiers einzugeben,
  3. lemon.markets Datenbank durchsuchen,
  4. Aufforderung an den*die Nutzer:in, das gewünschte Wertpapier aus der Liste auszuwählen,
  5. Aufforderung an den*die Nutzer:in, die Seite anzugeben, d.h. "kaufen" oder "verkaufen",
  6. Aufforderung an den*die Nutzer:in, die Menge anzugeben,
  7. Order aufgeben und den*die Benutzer:in zur Bestätigung auffordern,
  8. Order aktivieren und Bestätigungsnachricht senden.

In diesem Artikel werden wir sechs zusätzliche states einrichten: TYPE, REPLY, NAME, ISIN, SIDE und QUANTITY. Wir haben noch einen weiten Weg vor uns, also lass uns gleich loslegen!

So könnte deine Konversation mit dem @LemonTraderBot aussehen.

Den Bot einrichten 🤖

Wenn Du mitmachen willst, findest Du das GitHub-Repository hier

Auswählen des Wertpapiertyps: TYPE 🎸

Eine weitere nützliche Funktion der Telegram API (und des python-telegram-bot Wrappers) ist die Möglichkeit, eigene Tastaturen zu erstellen. Um die Benutzerfreundlichkeit zu erhöhen, kannst Du Schaltflächen mit vordefinierten Antworten erstellen, die der*die Benutzer:in antippen kann, anstatt eine eigene Antwort zu formulieren. Für die Auswahl eines Wertpapiertyps stehen nur wenige Optionen zur Verfügung, daher ist dies das perfekte Szenario für die Verwendung einer eigenen Tastatur. Nehmen wir an, wir erlauben unserem Bot nur den Handel mit Aktien und ETFs:

1reply_keyboard = [['Stock', 'ETF']]

Die Tastatur wird wie folgt implementiert:

1update.message.reply_text(
2    'Authentication successful.\n\n'
3    'What type of instrument do you want to trade?',
4    reply_markup=ReplyKeyboardMarkup(
5        reply_keyboard, one_time_keyboard=True,
6    ),
7)

Diese Änderungen werden an der Methode get_client_secret() in der Klasse TradingBot.py vorgenommen. Und um die Konversation fortzusetzen, müssen wir den nächsten state zurückgeben (d.h. TradingBot.TYPE), anstatt die Konversation zu beenden (wie wir es im ersten Artikel getan haben). 

PS: Achte darauf, dass Du TYPE am Anfang Deines Skripts initialisierst! (zusammen mit allen anderen states)

Suchen mit lemon.markets: REPLY, NAME & ISIN 🍋

Wir werden drei neue Methoden in TradingBot.py einführen: get_search_query(), get_instrument_name() und get_isin().

1def get_search_query(self, update: Update, context: CallbackContext) -> int:
2    """Prompts user to enter instrument name."""
3    # store user response in dictionary with key 'type'
4    context.user_data['type'] = update.message.text.lower()
5    update.message.reply_text(
6        f'What is the name of the {context.user_data["type"]} you would like to trade?')
7    print(f'user data: {context.user_data}')
8    return TradingBot.REPLY
9def get_instrument_name(self, update: Update, context: CallbackContext) -> int:
10    """Searches for instrument and prompts user to select an instrument."""
11    context.user_data['search_query'] = update.message.text.lower()
12    print(f'user data: {context.user_data}')
13        
14    try:
15        instruments = Instrument(context.user_data['access_token']).
16            get_titles(context.user_data['search_query'], context.user_data['type'])
17    except Exception as e:
18        print(e)
19        update.message.reply_text(
20            "There was an error, ending the conversation."
21            "If you'd like to try again, send /start.")
22        return ConversationHandler.END
23    titles = list(instruments.keys())
24    reply_keyboard = [titles]
25    update.message.reply_text(
26        f'Please choose the instrument you wish to trade.',
27        reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
28    )
29    return TradingBot.NAME
30def get_isin(self, update: Update, context: CallbackContext) -> int:
31    """Retrieves ISIN and prompts user to select side (buy/sell)."""
32    text = update.message.text
33    print(f'user data: {context.user_data}')
34    try:
35        instruments = Instrument(context.user_data['access_token']).
36            get_titles(context.user_data['search_query'], context.user_data['type'])
37    except Exception as e:
38        print(e)
39        update.message.reply_text(
40            "There was an error, ending the conversation."
41            "If you'd like to try again, send /start.")
42        return ConversationHandler.END
43    context.user_data['title'] = text
44    context.user_data['isin'] = instruments.get(text)
45    reply_keyboard = [['Buy', 'Sell']]
46    update.message.reply_text(
47        f'Would you like to buy or sell {context.user_data["title"]}?',
48        reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
49    )
50    return TradingBot.ISIN

Die erste Funktion, get_search_query(), ist ziemlich selbsterklärend: Wir speichern den Textinput (bezüglich des Typs) in user_data und fordern den*die Benutzer:in auf, den Namen der Aktie oder des ETF einzugeben.

Die Funktion get_instrument_name() ist etwas komplizierter, vor allem weil wir auf eine neue Klasse treffen, die wir noch nicht definiert haben: Instrument (entspricht dem Instrument-Endpunkt von lemon.markets). Die nächste Funktion, get_isin(), verwendet dieselbe Instrument Klasse, also schauen wir uns die Klasse zuerst an.

1import os
2from helpers import RequestHandler
3class Instrument(RequestHandler):
4    def get_titles(self, search_query: str, instrument_type: str):
5        endpoint = f'instruments/?search={search_query}&type={instrument_type}'
6        response = self.get_data_market(endpoint)
7        results = response['results']
8        instruments: dict = {}
9        if len(results) <= 3:
10            for result in results:
11                instruments[result['title']] = result['isin']
12        else:
13            for result in results[:4]:
14                instruments[result['title']] = result['isin']
15        return instruments
16			
17    def get_price(self, isin: str):
18        mic = os.getenv("MIC")
19        endpoint = f'quotes/?from=latest&mic={mic}&isin={isin}'
20        response = self.get_data_market(endpoint)
21        print(response)
22        bid = response['results'][0]['b']
23        ask = response['results'][0]['a']
24        return bid, ask

Wie Du siehst, ahmt die Klasse Instrument die Klasse Token nach - sie verweist auf einen bestimmten lemon.markets-Endpunkt und ruft eine Antwort auf der Grundlage von Textinput ab. In diesem Fall haben wir zwei Methoden: get_titles() und get_price(). Erstere ruft ein Wörterbuch mit Wertpapieren (key) und ISINs (value) der drei besten Suchergebnisse nach einer bestimmten Abfrage ab und letztere ruft ein Tupel aus Geld- und Briefkurs auf Basis einer ISIN ab. 

Um noch einmal auf die Methoden im vorherigen Code Snippet zurückzukommen: get_instrument_name() baut eine Antworttastatur aus Wörterbuch keys auf. Je nachdem, auf welche Schaltfläche der*die Benutzer:in klickt, ruft get_isin() die ISIN (den value) ab, die mit diesem Titel verbunden ist. Beachte, dass wir alle Requests an die API innerhalb eines try-except-Blocks platzieren, um ungültige Antworten der API abzufangen (die auftreten können, wenn wir sie mit unerwarteten Informationen füttern). 

Die Order vorbereiten: SEITE, MENGE 💁

Da wir jetzt wissen, welches Wertpapier gehandelt werden soll, müssen wir noch festlegen, in welche Richtung (Kauf oder Verkauf) und wie viele.

1def get_side(self, update: Update, context: CallbackContext) -> int:
2    """Retrieves total balance (buy) or amount of shares owned (sell), most recent price and prompts user to
3    indicate quantity. """
4    context.user_data['side'] = update.message.text.lower()
5    print(f'user data: {context.user_data}')
6    try:
7        [context.user_data['bid'], context.user_data['ask']] = Instrument(context.user_data['access_token']).\
8            get_price(context.user_data['isin'])         
9        context.user_data['balance'] = Space(context.user_data['access_token']).\
10            get_balance(context.user_data['space_uuid'])
11    except Exception as e:
12        print(e)
13        update.message.reply_text(
14            "There was an error, ending the conversation. If you'd like to try again, send /start.")
15        return ConversationHandler.END
16    # if user chooses buy, present ask price, total balance and ask how many to buy
17    if context.user_data['side'] == 'buy':
18        update.message.reply_text(
19            f'This instrument is currently trading for €{context.user_data["ask"]}, your total balance is '
20            f'€{round(context.user_data["balance"], 2)}. '
21            f'How many shares do you wish to {context.user_data["side"]}?'
22        )
23    # if user chooses sell, retrieve how many shares owned
24    else:
25       positions = Portfolio(context.user_data['access_token']).get_portfolio(context.user_data['space_uuid'], )
26       # initialise shares owned to 0
27        context.user_data['shares_owned'] = 0
28       # if instrument in portfolio, update shares owned
29       for position in positions:
30            if position['instrument']['isin'] == context.user_data['isin']:
31                context.user_data['shares_owned'] = position['quantity']
32       update.message.reply_text(
33           f'This instrument can be sold for €{round(context.user_data["bid"], 2)}, you currently own '
34            f'{context.user_data["shares_owned"]} share(s). '
35            f'How many shares do you wish to {context.user_data["side"]}?'
36        )
37    return TradingBot.SIDE
38def get_quantity(self, update: Update, context: CallbackContext) -> int:
39    """Processes quantity, places order (if possible) and prompts
40    user to confirm order. """
41    context.user_data['quantity'] = float(update.message.text.lower())
42    print(f'user data: {context.user_data}')
43    reply_keyboard = [['Confirm', 'Cancel']]
44    # determine total cost of buy or sell
45    if context.user_data['side'] == 'buy':
46        context.user_data['total'] = context.user_data['quantity'] * float(context.user_data['ask'])
47    else:
48        context.user_data['total'] = context.user_data['quantity'] * float(context.user_data['bid'])
49    valid_time = (datetime.datetime.now() + datetime.timedelta(hours=1)).timestamp()
50    else:
51        try:
52            # ensure you have a valid token
53            context.user_data['access_token'] = Token().authenticate(
54                context.user_data['client_id'],
55                context.user_data['client_secret']
56            ).get('access_token')
57            # place order
58            context.user_data['order_uuid'] = \
59                Order(token=context.user_data['access_token']).place_order(
60                    isin=context.user_data['isin'],
61                    valid_until=valid_time,
62                    side=context.user_data['side'],
63                    quantity=context.user_data['quantity'],
64                    space_uuid=context.user_data['space_uuid']
65                )['uuid']
66        except Exception as e:
67            print(e)
68            update.message.reply_text(
69                "There was an error, ending the conversation. If you'd like to try again, send /start.")
70            return ConversationHandler.END
71        update.message.reply_text(
72            f'You\'ve indicated that you wish to {context.user_data["side"]} {int(context.user_data["quantity"])} '
73            f'share(s) of {context.user_data["title"]} at a total of €{round(context.user_data["total"], 2)}. '
74            f'Please confirm or cancel your order to continue.',
75            reply_markup=ReplyKeyboardMarkup(
76                reply_keyboard, one_time_keyboard=True,
77            )
78        )
79        return TradingBot.QUANTITY

Im obigen Code Snippet führen wir drei neue Klassen ein: Space, Portfolio und Order, die alle den Endpunkt Spaces verwenden. Der obige Code folgt demselben Format, das wir bisher implementiert haben. Um Wiederholungen zu vermeiden, wollen wir uns stattdessen ansehen, was diese Klassen beinhalten.

1from helpers import RequestHandler
2class Space(RequestHandler):
3    def get_space_uuid(self,):
4        endpoint = f'spaces'
5        response = self.get_data_trading(endpoint)['results']
6        return response[0]['uuid']
7    def get_balance(self, space_uuid):
8        endpoint = f'spaces/{space_uuid}/'
9        response = self.get_data_trading(endpoint)
10        return float(response['state']['cash_to_invest'])

Die Klasse Space hat zwei Funktionen: get_space_uuid() und get_balance(). Aus der API-Antwort geben wir die space_uuid und das cash_to_invest zurück. Hinweis: Die Funktion get_space_uuid() wird innerhalb der Funktion get_client_secret() in der TradingBot Klasse verwendet, um die space_uuid in user_data zu speichern. Sieh Dir das GitHub-Repository an, um mehr zu erfahren.

1from helpers import RequestHandler
2class Portfolio(RequestHandler):
3    def get_portfolio(self, space_uuid) -> list:
4        endpoint = f'spaces/{space_uuid}/portfolio/'
5        response = self.get_data_trading(endpoint)
6        return response['results']

Die Portfolio Klasse ruft alle aktuellen Positionen ab (einschließlich Informationen wie den Durchschnittspreis). Schaue gerne in unsere Dokumentation, um den API Output zu sehen.

1from helpers import RequestHandler
2class Order(RequestHandler):
3    def place_order(self, isin: str, valid_until: float, quantity: int, side: str, space_uuid: str):
4        order_details = {
5            "isin": isin,
6            "valid_until": valid_until,
7            "side": side,
8            "quantity": quantity,
9        }
10        endpoint = f'spaces/{space_uuid}/orders/'
11        response = self.post_data(endpoint, order_details)
12        return response
13    def activate_order(self, order_uuid: str, space_uuid: str):
14        endpoint = f'spaces/{space_uuid}/orders/{order_uuid}/activate/'
15        response = self.put_data(endpoint)
16        return response
17    def get_order(self, order_uuid: str, space_uuid: str):
18        endpoint = f'spaces/{space_uuid}/orders/{order_uuid}/'
19        response = self.get_data_trading(endpoint)
20        return response

Die Klasse Order kann eine Order aufgeben, aktivieren und abrufen. Wenn Du diese Klassen in Aktion sehen willst, schau dir einfach das Repo an!

Wir verwenden alle drei oben genannten Klassen in der TradingBot Klasse. Wie Du im Code Snippet ganz oben in diesem Abschnitt sehen kannst, haben wir die Funktionen get_side() und get_quantity(). Je nachdem, ob der*die Nutzer:in sich entscheidet, das Wertpapier zu kaufen oder zu verkaufen, kann die get_side() Funktion zwei verschiedene Wege einschlagen. Space.get_balance() und Instrument.get_price() werden aufgerufen, um das verfügbare Guthaben und den aktuellen Preis des ausgewählten Wertpapiers zu ermitteln. Diese beiden Informationen werden dem*der Nutzer:in angezeigt, wenn er*sie die Menge angeben soll. Schließlich können diese Informationen auch dazu verwendet werden, Orders einzuschränken, wenn sie die verfügbaren Mittel überschreiten (wir implementieren das nicht in diesem Blog-Beitrag, aber Du findest es in unserem GitHub-Repository).

Entscheidet sich der*die Nutzer:in für eine Kauforder, wird der Briefkurs angezeigt und es werden keine weiteren Aktionen durchgeführt. Entscheidet sich der*die Nutzer:in für einen Verkauf, wird die Klasse Portfolio aufgerufen, um alle aktuellen Positionen abzufragen. Dann wird der Geldkurs zusammen mit der Anzahl der aktuell gehaltenen Positionen angezeigt. 

TradingBot.get_quantity() speichert die Menge und platziert die Order mit einem aktuellen Access Token. Der letzte Schritt ist die Aktivierung der Order. 

Aktivieren der Order: BESTÄTIGUNG ✅

Bei lemon.markets möchten wir sicherstellen, dass Du immer über Deine platzierten Orders informiert wirst. Nachdem eine Order platziert wird, muss sie auch aktiviert werden - das geschieht in der Funktion confirm_order().

1def confirm_order(self, update: Update, context: CallbackContext) -> int:
2        """Activates order (if applicable), displays purchase/sale price and prompts user to indicate whether any
3        additional trades should be made. """
4        context.user_data['order_decision'] = update.message.text
5        print(f'user data: {context.user_data}')
6        if context.user_data['order_decision'] == 'Cancel':
7            update.message.reply_text(
8                'You\'ve cancelled your order.'
9            )
10        else:
11            try:
12                Order(context.user_data['access_token']).activate_order(
13                    context.user_data['order_uuid'],
14                    context.user_data['space_uuid']
15                )
16            except Exception as e:
17                print(e)
18                update.message.reply_text(
19                    "There was an error, ending the conversation. If you'd like to try again, send /start.")
20                return ConversationHandler.END
21            # keep checking order status until executed so that execution price can be retrieved
22            update.message.reply_text(
23                'Please wait while we process your order.'
24            )
25            while True:
26                order_summary = Order(context.user_data['access_token']).get_order(
27                    context.user_data['order_uuid'],
28                    context.user_data['space_uuid']
29                )
30                if order_summary.get('status') == 'executed':
31                    print('executed')
32                    break
33                time.sleep(1)
34            context.user_data['average_price'] = order_summary.get('average_price')
35            update.message.reply_text(
36                f'Your order was executed at €{round(float(context.user_data["average_price"]), 2)} per share. '
37            )
38        return ConversationHandler.END

Darüber hinaus ruft die Funktion die Orderinformationen so lange ab, bis der Status von "Activated" auf "Executed" wechselt. Erst dann kann der endgültige Kauf- oder Verkaufspreis abgerufen werden. Anschließend sendet der Bot eine Bestätigungsnachricht und die Konversation ist beendet. 

Der letzte Schritt besteht darin, diese states und Funktionen in unseren ConversationHandler einzufügen. Das können wir jetzt tun.

1conv_handler = ConversationHandler(
2        # initiate the conversation
3        entry_points=[CommandHandler('start', TradingBot().start)],
4        # different conversation steps and handlers that should be used if user sends a message
5        # when conversation with them is currently in that state
6        states={
7            TradingBot.ID: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_client_id)],
8            TradingBot.SECRET: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_client_secret)],
9            TradingBot.TYPE: [
10                MessageHandler(
11                    Filters.regex('^(Stock|stock|Bond|bond|Fund|fund|ETF|etf|Warrant|warrant)$') & ~Filters.regex('^/'),
12                    TradingBot().get_search_query)],
13            TradingBot.REPLY: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_instrument_name)],
14            TradingBot.NAME: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_isin)],
15            TradingBot.ISIN: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_side)],
16            TradingBot.SIDE: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().get_quantity)],
17            TradingBot.QUANTITY: [MessageHandler(Filters.text & ~Filters.regex('^/'), TradingBot().confirm_order)],
18        },
19        # if user currently in conversation but state has no handler or handle inappropriate for update
20        fallbacks=[CommandHandler(('cancel', 'end'), TradingBot().cancel)],
21    )

So, das waren eine Menge Informationen und einige Themen aufgrund des Projektumfangs vielleicht etwas schwierig zu verstehen. Wenn Du eher ein visueller Lerntyp bist, haben wir auch ein YouTube-Tutorial erstellt, das Dich durch den gesamten Prozess führt. Und wenn Du lieber nur den Code sehen möchten, gucke Dir unser GitHub-Repository mit allem, was wir in diesem Beitrag erwähnt haben, an (zusammen mit einigen anderen nützlichen Funktionen). Du kannst das Repository auch klonen und das Projekt auf Deinem lokalen Gerät neu erstellen.

Weitere Features

Das Tolle an diesem Projekt ist, dass es immer wieder mit neuen Funktionen erweitert werden kann. Wir haben noch einige zusätzliche Funktionen in unseren Bot implementiert, wie zum Beispiel (aber nicht ausschließlich):

  • Bei der Suche nach einem Wertpapier kann der*die Benutzer:in "Other" auswählen, wenn das Wertpapier nicht in der Liste enthalten ist. Dann wird der*die Benutzer:in aufgefordert, seine Suchanfrage erneut einzugeben
  • Wenn der*die Benutzer:in eine nicht ganzzahlige Zahl bei der Menge eingibt, kommt eine Fehlermeldung (bisher unterstützt lemon.markets keine Fractional Shares)
  • Beim Platzieren einer Kauforder kommt eine Fehlermeldung, wenn mehr gekauft wird, als das verfügbare Guthaben erlaubt (lemon.markets hat keine Margin Accounts) 
  • Beim Platzieren einer Verkaufsorder kommt eine Fehlermeldung, wenn mehr verkauft wird, als der*die Benutzer:in besitzt (lemon.markets unterstützt keine Leerverkäufe) 
  • Zwischen user_data und chat_data unterscheiden. Alle benutzerspezifischen Informationen , wie z.B. Client ID, Client Secret, Access Token und Space UUID in ersterem speichern und alle orderspezifischen Informationen in letzterem. Wenn die Order abgeschlossen ist, einfach die chat_data löschen und der*die Benutzer:in muss sich nicht erneut anmelden, um einen anderen Trade zu tätigen.

Abgesehen davon gibt es noch viele weitere Funktionen, die man implementieren könnte, wie z.B.:

  • Implementierung eines Befehls, der einen Aktienkauf auf der Grundlage technischer Signale vorschlägt
  • Dem Benutzer die Möglichkeit geben, schnell eine Order zu platzieren, indem er "10 Tesla-Aktien kaufen" schreibt, anstatt die gesamte Konversation zu durchlaufen
  • Implementierung eines Befehls, der die relative und/oder absolute Performance ausgibt.

Wenn Du eine dieser Funktionen implementieren möchtest, lass es uns wissen! Oder mache einen Pull-Request an das GitHub-Repository, wir würden gerne sehen, was Du gebaut hast. 

Wir hoffen, dass dieser Artikel als Inspiration für die Integration von lemon.markets in Deinen eigenen Telegram-Bot gedient hat. Du findest den Inhalt dieses Blog Posts auch nochmal als Video auf unserem YouTube-Kanal (zusammen mit einigen anderen interessanten Projekten). Und vergiss nicht, das entsprechende GitHub-Repository zu besuchen. 

Unsere Warteliste wird immer länger, hast Du Dich schon angemeldet? Wir würden uns freuen, Dich an Bord zu haben. Und erzähl uns gerne, was Du baust! Sei es über Slack oder E-mail

Joanne 🍋.

Das könnte Dich auch interessieren

Mit OpenFIGI und lemon.markets ein Tickersymbol einer ISIN zuordnen

blog photo

Wenn Du an verschiedenen Börsen tradest, wirst Du feststellen, dass diese meist unterschiedliche Methoden haben um Wertpapiere zu identifizieren. Die US-Börsen verwenden beispielsweise häufig Ticker, während die deutschen Börsen auf eine ISIN verweisen. Und manchmal ist das Wechseln zwischen diesen Identifiern nicht so einfach, wie man es erwarten würde. Stattdessen kannst Du den Prozess automatisieren, indem Du einen (weniger als 10 Zeilen) Code schreibst, der die "Übersetzung" für Dich übernimmt. Lies weiter, um zu erfahren, wie Du die OpenFIGI- und lemon.markets-APIs nutzen kannst, um einem Ticker die entsprechenden ISIN zuzuordnen.

10 Fehler beim (automatisierten) Trading und wie Du sie vermeidest

blog photo

Hallo! Mein Name ist Joanne und ich bin Teil des lemon.markets-Teams in Berlin. Wir bauen eine Trading API, mit der Entwickler:innen ihre eigene Trading Experience gestalten können. Außerdem wollen wir eine Umgebung schaffen, in der Nutzer:innen sich zu der Schnittmenge von  Algorithmus-Entwicklung und dem Aktienmarkt austauschen können. Wir wollen zu dieser Diskussion auch etwas beitragen und haben deshalb eine Liste mit zehn Fehlern zusammengestellt, die Anfänger im automatisierten Handel (um ehrlich zu sein, manchmal auch erfahrene Profis) machen können. Damit sie Dir nicht passieren, haben wir zusätzlich erklärt, wie Du sie vermeiden kannst.

Integration von lemon.markets in Deinen Telegram-Bot (Teil 2 von 2)

blog photo

Hallo! Ich bin Joanne und gehöre zum Team von lemon.markets. Wir sind ein Berliner Start-up, das automatisiertes Trading über APIs ermöglicht. Unser Ziel ist es, Entwicklern und Entwicklerinnen alle Werkzeuge zur Verfügung zu stellen, mit denen sie ihre eigene Trading Experience an der Börse bauen können. Dabei gibt es hunderte Use-Cases für unser Produkt, wie beispielsweise ein eigenes Frontend zum Platzieren von Orders. Dafür kannst Du bei Null anfangen oder einen bereits existierenden Dienst wie beispielsweise Telegram nutzen. In diesem Artikel erweitere ich das Projekt, das wir in unserem letzten Artikel vorgestellt haben.

Tiefer eintauchen

Finde weitere Ressourcen für einen einfachen Einstieg

In unserer Dokumentation erfahrt Ihr mehr über unsere API-Struktur, die verschiedenen Endpunkte und spezifische Anwendungsfälle.

Austauschen

Tritt der lemon.markets Community bei

Tritt unserem Slack-Channel bei, um Dich aktiv an unserer Community zu beteiligen, Fragen an andere Nutzer:innen zu stellen und immer auf dem Laufenden zu bleiben.

Team unterstützen

Lust lemon.markets mit uns zu bauen?

Wir sind immer auf der Suche nach großartigen Ergänzungen für unser Team, die uns beim Aufbau einer Brokerage Infrastruktur für das 21. Jahrhundert helfen.

Products
Pricing
For Developers
SlackGithubBlog
© lemon.markets 2021Privacy PolicyImprint
All systems normal