The OpenBB Bot is a chatting bot that retrieves financial data on Discord and Telegram.
If you want to use our hosted version which is 100% free, go to http://my.openbb.co/app/bot.
Otherwise, the remainder of this document will explain how you can self-host our Discord bot.
We have open source our framework so that users are able to build their own Discord bots. We are relying on the OpenBB Platform and Disnake.
- Go to https://discord.com/developers/applications
- Click on "New Application"
- Select a name for the application and agree to the ToS
- Add an image so that you can easily recognize the bot on Discord, and save changes
- On the "Settings" sidebar, go to "OAuth2"
- Copy the ClientID on that page
- Go to https://discord.com/oauth2/authorize?client_id=&scope=bot and replace
<CLIENTID>
by the one you copied in the previous step - Make sure you select the Server you are interested in having the bot in
- On the "Settings" sidebar, go to "Bot"
- Click on "Reset Token" and copy the Token ID - this will be necessary when running the bot.
- Update and rename the
.env.example
file to.env
- In this
.env
file setDISCORD_BOT_TOKEN
with the Token ID previously copied - If you don't have an OpenBB Hub account, go to http://my.openbb.co
- Once you do go the OpenBB Platform - PAT (personal access token) page and copy the PAT
- In the
.env
file setOPENBB_HUB_PAT
with the PAT previously copied
One of the reasons you rely on the OpenBB Hub PAT is that it manages all of your API keys on your behalf once you want to access data using OpenBB. So make sure you have API keys set on the Credentials page.
- Install the requirements
poetry install
- Run your own OpenBB Bot
uvicorn main:app --reload
Create a new file or edit a pre-existing file in the folder: bot/cmds
.
Let's assume that we create a new file called stockCandlestick_cmds.py
. We will go step-by-step over what this file should contain:
- Import all necessary python libraries
import traceback
from openbb import obb
import disnake
from disnake.ext import commands
from datetime import datetime, timedelta
from bot.showview import ShowView
from utils.pywry_figure import PyWryFigure
- Create a new class associated with the file which may hold multiple commands
class CandlestickChartsCommands(commands.Cog):
"""Candlestick Charting commands."""
- Within that class, initialize the class as follows
def __init__(self, bot: commands.Bot):
self.bot = bot
- Within that class create a command. An example with several comments to explain the structure follows
# Ensure that the name and the method name is the same
# this will be what the user utilizes to invoke the command from Discord
@commands.slash_command(name="candle")
async def candle(
self, # inherits from the class
inter: disnake.AppCmdInter, # comes from disnake and is used to run the command
# custom commands
ticker: str, # user can type anything but this parameter is required
interval: str = commands.Param( # user can only select between '1day', '15min' and '5min'
choices=[
"1day",
"15min",
"5min",
],
default="1day", # if the user doesn't select any, by default '1day' is set
),
days: int = 200, # user has to select an integer, by default 200 is selected
):
"""Shows a daily candlestick chart for the ticker provided.
Parameters
-----------
ticker: Stock Ticker
interval: Select whether to show 1day, 15min, or 5min intervals
days: Number of days in the past to show
"""
# Ensure the docstring above is added
# so that the users know what each command and parameter corresponds to.
try:
# Ensures the data can be retrieved with disnake
await inter.response.defer()
# Handle hardcoded parameters, e.g. data provider coming from OpenBB
provider = "fmp" # can also be 'polygon' or 'intrinio'
# Pre-processing of parameters, in case it needs to be processed before calling OpenBB
ticker = ticker.upper()
params = {
"symbol": ticker,
"provider": provider,
"start_date": (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d"),
"end_date": datetime.now().strftime("%Y-%m-%d"),
"interval": interval,
"chart": True,
}
# Get the data from OpenBB
data = obb.equity.price.historical(**params).chart.content
# Format this data to be displayed on Discord
title = f"{ticker} {interval.replace('1day', 'Daily')}"
fig = (
PyWryFigure()
.update(data)
.update_layout(
margin=dict(l=80, r=10, t=40, b=20),
paper_bgcolor="#111111",
plot_bgcolor="rgba(0,0,0,0)",
height=762,
width=1430,
title=dict(text=title, x=0.5),
xaxis=dict(tick0=0.5, tickangle=0),
)
)
y_min, y_max = min(fig.data[0].low), max(fig.data[0].high)
y_range = y_max - y_min
y_min -= y_range * 0.2
y_max += y_range * 0.08
fig.update_layout(yaxis=dict(range=[y_min, y_max], autorange=False))
# The response must be a Dictionary with key "plots", "embeds" or "images_list"
response: dict = {"plots": fig.prepare_image()}
except Exception as e:
# In case there's an exception we want the error to be printed in the user's console
traceback.print_exc()
# This returns the error as a string to the Discord so that the user can see what happened
# this is extremely useful when debugging
return await ShowView().discord(inter, "candle", str(e), error=True)
This handles the command rendering on Discord
await ShowView().discord(inter, "candle", response, no_embed=True)
-
Within that class, create as many methods as you wish
-
Finally, create a
setup
function that adds this class to the OpenBB Bot instance
def setup(bot: commands.Bot):
bot.add_cog(CandlestickChartsCommands(bot))