Adding the due diligence context (#936)

* Adding the due diligence context

This pr adds the due diligence commands, which don't require api keys, to the discord bot.

* Fixing arktrade table
This commit is contained in:
northern-64bit
2021-11-12 18:18:29 +01:00
committed by GitHub
parent 87238c26e1
commit 4bae7d4ee1
11 changed files with 707 additions and 7 deletions

View File

@@ -34,6 +34,7 @@ gst_bot.load_extension("generic_commands")
gst_bot.load_extension("economy.economy_menu")
gst_bot.load_extension("stocks.dark_pool_shorts.dps_menu")
gst_bot.load_extension("stocks.technical_analysis.ta_menu")
gst_bot.load_extension("stocks.due_diligence.dd_menu")
# Get all command names
all_cmds = gst_bot.all_commands.keys()

View File

@@ -177,11 +177,17 @@ class EconomyCommands(discord.ext.commands.Cog):
await msg.remove_reaction(emoji, ctx.bot.user)
except asyncio.TimeoutError:
text = text + "\n\nCOMMAND TIMEOUT."
embed = discord.Embed(title=title, description=text)
await msg.edit(embed=embed)
for emoji in emoji_list:
await msg.remove_reaction(emoji, ctx.bot.user)
embed = discord.Embed(
description="Error timeout - you snooze you lose! 😋",
colour=cfg.COLOR,
title="TIMEOUT Economy Menu",
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
def setup(bot: discord.ext.commands.Bot):

View File

@@ -169,7 +169,7 @@ class DarkPoolShortsCommands(discord.ext.commands.Cog):
"\n!stocks.dps <TICKER>"
)
title = "Dark Pool Shorts (DPS) Menu"
title = "Stocks: Dark Pool Shorts (DPS) Menu"
embed = discord.Embed(title=title, description=text, colour=cfg.COLOR)
embed.set_author(
name=cfg.AUTHOR_NAME,
@@ -229,11 +229,17 @@ class DarkPoolShortsCommands(discord.ext.commands.Cog):
await msg.remove_reaction(emoji, ctx.bot.user)
except asyncio.TimeoutError:
text = text + "\n\nCommand timeout."
embed = discord.Embed(title=title, description=text)
await msg.edit(embed=embed)
for emoji in emoji_list:
await msg.remove_reaction(emoji, ctx.bot.user)
embed = discord.Embed(
description="Error timeout - you snooze you lose! 😋",
colour=cfg.COLOR,
title="TIMEOUT Stocks: Dark Pool Shorts (DPS) Menu",
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
def setup(bot: discord.ext.commands.Bot):

View File

@@ -0,0 +1,46 @@
import discord
import config_discordbot as cfg
from gamestonk_terminal.stocks.due_diligence import finviz_model
async def analyst_command(ctx, ticker=""):
"""Displays analyst recommendations [Finviz]"""
try:
# Debug
if cfg.DEBUG:
print(f"!stocks.dd.analyst {ticker}")
# Check for argument
if ticker == "":
raise Exception("Stock ticker is required")
df = finviz_model.get_analyst_data(ticker)
report = "```" + df.to_string() + "```"
embed = discord.Embed(
title="Stocks: [Finviz] Analyst Recommendations",
description=report,
colour=cfg.COLOR,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [Finviz] Analyst Recommendations",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,95 @@
import discord
import config_discordbot as cfg
from helpers import pagination
import pandas as pd
from gamestonk_terminal.stocks.due_diligence import ark_model
async def arktrades_command(ctx, ticker="", num=""):
"""Displays trades made by ark [cathiesark.com]"""
try:
# Debug user input
if cfg.DEBUG:
print(f"!stocks.dd.arktrades{ticker}")
if num == "":
pass
else:
if not num.lstrip("-").isnumeric():
raise Exception("Number has to be an integer")
num = float(num)
if ticker == "":
raise Exception("A ticker is required")
ark_holdings = ark_model.get_ark_trades_by_ticker(ticker)
if ark_holdings.empty:
raise Exception(
"Issue getting data from cathiesark.com. Likely no trades found.\n"
)
ark_holdings = ark_holdings.drop(columns=["ticker"])
ark_holdings["Total"] = ark_holdings["Total"] / 1_000_000
ark_holdings.rename(
columns={"Close": "Close ($)", "Total": "Total ($1M)"}, inplace=True
)
ark_holdings.index = pd.Series(ark_holdings.index).apply(
lambda x: x.strftime("%Y-%m-%d")
)
if num == "":
ark_holdings_str = ark_holdings.to_string()
else:
ark_holdings_str = ark_holdings.head(num).to_string()
if len(ark_holdings_str) <= 4000:
embed = discord.Embed(
title=f"Stocks: [cathiesark.com] {ticker} Trades by Ark",
description="```" + ark_holdings_str + "```",
colour=cfg.COLOR,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
else:
i = 0
str_start = 0
str_end = 4000
columns = []
while i <= len(ark_holdings_str) / 4000:
columns.append(
discord.Embed(
title=f"Stocks: [cathiesark.com] {ticker} Trades by Ark",
description="```" + ark_holdings_str[str_start:str_end] + "```",
colour=cfg.COLOR,
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
)
str_end = str_start
str_start += 4000
i += 1
await pagination(columns, ctx)
except Exception as e:
embed = discord.Embed(
title=f"ERROR Stocks: [cathiesark.com] {ticker} Trades by Ark",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,51 @@
import discord
import config_discordbot as cfg
from gamestonk_terminal.stocks.due_diligence import csimarket_model
async def customer_command(ctx, ticker=""):
"""Displays customers of the company [CSIMarket]"""
try:
# Debug user input
if cfg.DEBUG:
print(f"!stocks.dd.customer {ticker}")
if ticker == "":
raise Exception("A ticker is required")
tickers = csimarket_model.get_customers(ticker)
if tickers == "":
raise Exception("Enter a valid ticker")
# Debug user output
if cfg.DEBUG:
print(tickers)
# Output data
embed = discord.Embed(
title="Stocks: [CSIMarket] Company Customers",
description=tickers,
colour=cfg.COLOR,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [CSIMarket] Company Customers",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,205 @@
import discord
from discordbot import gst_bot
import config_discordbot as cfg
import asyncio
from stocks.due_diligence.analyst import analyst_command
from stocks.due_diligence.pt import pt_command
from stocks.due_diligence.est import est_command
from stocks.due_diligence.sec import sec_command
from stocks.due_diligence.supplier import supplier_command
from stocks.due_diligence.customer import customer_command
from stocks.due_diligence.arktrades import arktrades_command
class DueDiligenceCommands(discord.ext.commands.Cog):
"""Due Diligence menu."""
def __init__(self, bot: discord.ext.commands.Bot):
self.bot = bot
@discord.ext.commands.command(name="stocks.dd.analyst")
async def analyst(self, ctx: discord.ext.commands.Context, ticker=""):
"""Displays analyst recommendations [Finviz]
Parameters
-----------
ticker: str
ticker, -h or help
"""
await analyst_command(ctx, ticker)
@discord.ext.commands.command(name="stocks.dd.pt")
async def pt(self, ctx: discord.ext.commands.Context, ticker="", raw="", start=""):
"""Displays chart with price targets [Business Insiders]
Parameters
-----------
ticker: str
ticker, -h or help
raw: boolean
True or false
start:
date (in date format for start date)
"""
await pt_command(ctx, ticker, raw, start)
@discord.ext.commands.command(name="stocks.dd.est")
async def est(self, ctx: discord.ext.commands.Context, ticker=""):
"""Displays earning estimates [Business Insider]
Parameters
-----------
ticker: str
ticker, -h or help
"""
await est_command(ctx, ticker)
@discord.ext.commands.command(name="stocks.dd.sec")
async def sec(self, ctx: discord.ext.commands.Context, ticker=""):
"""Displays sec filings [Market Watch]
Parameters
-----------
ticker: str
ticker, -h or help
"""
await sec_command(ctx, ticker)
@discord.ext.commands.command(name="stocks.dd.supplier")
async def supplier(self, ctx: discord.ext.commands.Context, ticker=""):
"""Displays suppliers of the company [CSIMarket]
Parameters
-----------
ticker: str
ticker, -h or help
"""
await supplier_command(ctx, ticker)
@discord.ext.commands.command(name="stocks.dd.customer")
async def customer(self, ctx: discord.ext.commands.Context, ticker=""):
"""Displays customers of the company [CSIMarket]
Parameters
-----------
ticker: str
ticker, -h or help
"""
await customer_command(ctx, ticker)
@discord.ext.commands.command(name="stocks.dd.arktrades")
async def arktrades(self, ctx: discord.ext.commands.Context, ticker="", num=""):
"""Displays trades made by ark [cathiesark.com]
Parameters
-----------
ticker: str
ticker, -h or help
num: int
number of rows displayed
"""
await arktrades_command(ctx, ticker, num)
@discord.ext.commands.command(name="stocks.dd")
async def economy(self, ctx: discord.ext.commands.Context, ticker=""):
"""Economy Context Menu
Returns
-------
Sends a message to the discord user with the commands from the dd context.
The user can then select a reaction to trigger a command.
"""
if cfg.DEBUG:
print("!stocks.dd")
if ticker == "":
embed = discord.Embed(
title="ERROR Stocks: Due Diligence (DD) Menu",
colour=cfg.COLOR,
description="A stock ticker is required",
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
text = (
f"0️⃣ !stocks.dd.analyst {ticker}\n"
f"1️⃣ !stocks.dd.pt {ticker} <RAW> <DATE_START>\n"
f"2️⃣ !stocks.dd.est {ticker}\n"
f"3️⃣ !stocks.dd.sec {ticker}\n"
f"4️⃣ !stocks.dd.supplier {ticker}\n"
f"5️⃣ !stocks.dd.customer {ticker}\n"
f"6️⃣ !stocks.dd.arktrades {ticker}"
)
title = "Stocks: Due Diligence (DD) Menu"
embed = discord.Embed(title=title, description=text, colour=cfg.COLOR)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
msg = await ctx.send(embed=embed)
emoji_list = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣"]
for emoji in emoji_list:
await msg.add_reaction(emoji)
def check(reaction, user):
return user == ctx.message.author and str(reaction.emoji) in emoji_list
try:
reaction, _ = await gst_bot.wait_for(
"reaction_add", timeout=cfg.MENU_TIMEOUT, check=check
)
if reaction.emoji == "0️⃣":
if cfg.DEBUG:
print("Reaction selected: 0")
await analyst_command(ctx, ticker)
elif reaction.emoji == "1️⃣":
if cfg.DEBUG:
print("Reaction selected: 1")
await pt_command(ctx, ticker)
elif reaction.emoji == "2️⃣":
if cfg.DEBUG:
print("Reaction selected: 2")
await est_command(ctx, ticker)
elif reaction.emoji == "3️⃣":
if cfg.DEBUG:
print("Reaction selected: 3")
await sec_command(ctx, ticker)
elif reaction.emoji == "4️⃣":
if cfg.DEBUG:
print("Reaction selected: 4")
await supplier_command(ctx, ticker)
elif reaction.emoji == "5️⃣":
if cfg.DEBUG:
print("Reaction selected: 5")
await customer_command(ctx, ticker)
elif reaction.emoji == "6️⃣":
if cfg.DEBUG:
print("Reaction selected: 6")
await arktrades_command(ctx, ticker)
for emoji in emoji_list:
await msg.remove_reaction(emoji, ctx.bot.user)
except asyncio.TimeoutError:
for emoji in emoji_list:
await msg.remove_reaction(emoji, ctx.bot.user)
embed = discord.Embed(
description="Error timeout - you snooze you lose! 😋",
colour=cfg.COLOR,
title="TIMEOUT Stocks: Due Diligence (DD) Menu",
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
def setup(bot: discord.ext.commands.Bot):
gst_bot.add_cog(DueDiligenceCommands(bot))

View File

@@ -0,0 +1,83 @@
import discord
import config_discordbot as cfg
from helpers import pagination
from gamestonk_terminal.stocks.due_diligence import business_insider_model
async def est_command(ctx, ticker=""):
"""Displays earning estimates [Business Insider]"""
try:
# Debug
if cfg.DEBUG:
print(f"!stocks.dd.est {ticker}")
# Check for argument
if ticker == "":
raise Exception("Stock ticker is required")
(
df_year_estimates,
df_quarter_earnings,
df_quarter_revenues,
) = business_insider_model.get_estimates(ticker)
if (
df_quarter_revenues.empty
and df_year_estimates.empty
and df_quarter_earnings.empty
):
raise Exception("Enter a valid ticker")
# Debug user output
if cfg.DEBUG:
print(df_year_estimates.to_string())
print(df_quarter_earnings.to_string())
print(df_quarter_revenues.to_string())
# Output data
cols = []
initial_text = (
"´´´Page 0: Overview\nPage 1: Year Estimates\n"
"Page 2: Quarter Earnings\nPage 3: Quarter Revenues´´´"
)
text = "´´´" + df_year_estimates.to_string() + "´´´"
cols.append(text)
text = "´´´" + df_quarter_earnings.to_string() + "´´´"
cols.append(text)
text = "´´´" + df_quarter_revenues.to_string() + "´´´"
cols.append(text)
columns = []
columns.append(
discord.Embed(
title="Stocks: [Business Insider] Earning Estimates",
description=initial_text,
colour=cfg.COLOR,
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
)
for text in cols:
columns.append(
discord.Embed(description=text, colour=cfg.COLOR,).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
)
await pagination(columns, ctx)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [Business Insider] Earning Estimates",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,104 @@
import discord
import config_discordbot as cfg
from discordbot import gst_imgur
from matplotlib import pyplot as plt
from datetime import datetime, timedelta
import os
import helpers
from gamestonk_terminal.stocks.due_diligence import business_insider_model
async def pt_command(ctx, ticker="", raw="", start=""):
"""Displays price targets [Business Insider]"""
try:
# Debug
if cfg.DEBUG:
print(f"!stocks.dd.pt {ticker}")
# Check for argument
if ticker == "":
raise Exception("Stock ticker is required")
if start == "":
start = datetime.now() - timedelta(days=365)
else:
start = datetime.strptime(start, cfg.DATE_FORMAT)
if raw == "false" or raw == "False" or raw == "FALSE" or raw == "":
raw = False
elif raw == "true" or raw == "True" or raw == "TRUE":
raw = True
else:
raise Exception("raw argument has to be true or false")
df_analyst_data = business_insider_model.get_price_target_from_analysts(ticker)
stock = helpers.load(ticker, start)
if df_analyst_data.empty or stock.empty:
raise Exception("Enter valid ticker")
# Output Data
if raw:
df_analyst_data.sort_index(ascending=False)
report = "´´´" + df_analyst_data.to_string() + "´´´"
embed = discord.Embed(
title="Stocks: [Business Insider] Price Targets",
description=report,
colour=cfg.COLOR,
).set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
ctx.send(embed=embed)
else:
if start:
df_analyst_data = df_analyst_data[start:]
plt.plot(stock.index, stock["Adj Close"].values, lw=3)
plt.plot(df_analyst_data.groupby(by=["Date"]).mean())
plt.scatter(
df_analyst_data.index, df_analyst_data["Price Target"], c="r", s=40
)
plt.legend(["Closing Price", "Average Price Target", "Price Target"])
plt.title(f"{ticker} (Time Series) and Price Target")
plt.xlim(stock.index[0], stock.index[-1])
plt.xlabel("Time")
plt.ylabel("Share Price")
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.savefig("ta_pt.png")
uploaded_image = gst_imgur.upload_image("ta_pt.png", title="something")
image_link = uploaded_image.link
if cfg.DEBUG:
print(f"Image URL: {image_link}")
title = "Stocks: [Business Insider] Price Targets " + ticker
embed = discord.Embed(title=title, colour=cfg.COLOR)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
embed.set_image(url=image_link)
os.remove("ta_pt.png")
await ctx.send(embed=embed)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [Business Insider] Price Targets",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,52 @@
import discord
import config_discordbot as cfg
from gamestonk_terminal.stocks.due_diligence import marketwatch_model
async def sec_command(ctx, ticker=""):
"""Displays sec filings [Market Watch]"""
try:
# Debug user input
if cfg.DEBUG:
print(f"!stocks.dd.sec {ticker}")
if ticker == "":
raise Exception("A ticker is required")
df_financials = marketwatch_model.get_sec_filings(ticker)
if df_financials.empty:
raise Exception("Enter a valid ticker")
# Debug user output
if cfg.DEBUG:
print(df_financials.to_string())
# Output data
report = "```" + df_financials.to_string() + "```"
embed = discord.Embed(
title="Stocks: [Market Watch] SEC Filings",
description=report,
colour=cfg.COLOR,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [Market Watch] SEC Filings",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)

View File

@@ -0,0 +1,51 @@
import discord
import config_discordbot as cfg
from gamestonk_terminal.stocks.due_diligence import csimarket_model
async def supplier_command(ctx, ticker=""):
"""Displays suppliers of the company [CSIMarket]"""
try:
# Debug user input
if cfg.DEBUG:
print(f"!stocks.dd.supplier {ticker}")
if ticker == "":
raise Exception("A ticker is required")
tickers = csimarket_model.get_suppliers(ticker)
if tickers == "":
raise Exception("Enter a valid ticker")
# Debug user output
if cfg.DEBUG:
print(tickers)
# Output data
embed = discord.Embed(
title="Stocks: [CSIMarket] Company Suppliers",
description=tickers,
colour=cfg.COLOR,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)
except Exception as e:
embed = discord.Embed(
title="ERROR Stocks: [CSIMarket] Company Suppliers",
colour=cfg.COLOR,
description=e,
)
embed.set_author(
name=cfg.AUTHOR_NAME,
icon_url=cfg.AUTHOR_ICON_URL,
)
await ctx.send(embed=embed)