diff --git a/discordbot/CONTRIBUTING.md b/bots/CONTRIBUTING.md
similarity index 100%
rename from discordbot/CONTRIBUTING.md
rename to bots/CONTRIBUTING.md
diff --git a/discordbot/README.md b/bots/README.md
similarity index 100%
rename from discordbot/README.md
rename to bots/README.md
diff --git a/discordbot/__init__.py b/bots/__init__.py
similarity index 100%
rename from discordbot/__init__.py
rename to bots/__init__.py
diff --git a/discordbot/cmds/cmds.py b/bots/cmds/cmds.py
similarity index 98%
rename from discordbot/cmds/cmds.py
rename to bots/cmds/cmds.py
index d33306a53dd..e1c280e6684 100644
--- a/discordbot/cmds/cmds.py
+++ b/bots/cmds/cmds.py
@@ -1,9 +1,9 @@
# pylint: disable=W0612
import disnake
from disnake.ext import commands
-from menus.menu import Menu
-import discordbot.config_discordbot as cfg
+import bots.config_discordbot as cfg
+from bots.menus.menu import Menu
class CmdsCommands(commands.Cog):
diff --git a/discordbot/cmds/dd_cmds.py b/bots/cmds/dd_cmds.py
similarity index 54%
rename from discordbot/cmds/dd_cmds.py
rename to bots/cmds/dd_cmds.py
index c520f939f00..9aad1d77514 100644
--- a/discordbot/cmds/dd_cmds.py
+++ b/bots/cmds/dd_cmds.py
@@ -1,32 +1,16 @@
from __future__ import annotations
import disnake
-import pandas as pd
from disnake.ext import commands
-from discordbot.config_discordbot import logger
-from discordbot.stocks.due_diligence.analyst import analyst_command
-from discordbot.stocks.due_diligence.arktrades import arktrades_command
-from discordbot.stocks.due_diligence.customer import customer_command
-from discordbot.stocks.due_diligence.est import est_command
-from discordbot.stocks.due_diligence.pt import pt_command
-from discordbot.stocks.due_diligence.sec import sec_command
-from discordbot.stocks.due_diligence.supplier import supplier_command
-
-
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "If you want", "to", "Live"]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
+from bots.helpers import ShowView, ticker_autocomp
+from bots.stocks.due_diligence.analyst import analyst_command
+from bots.stocks.due_diligence.arktrades import arktrades_command
+from bots.stocks.due_diligence.customer import customer_command
+from bots.stocks.due_diligence.est import est_command
+from bots.stocks.due_diligence.pt import pt_command
+from bots.stocks.due_diligence.sec import sec_command
+from bots.stocks.due_diligence.supplier import supplier_command
class DueDiligenceCommands(commands.Cog):
@@ -37,7 +21,7 @@ class DueDiligenceCommands(commands.Cog):
@commands.slash_command(name="dd-analyst")
async def analyst(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays analyst recommendations [Finviz]
@@ -46,13 +30,11 @@ class DueDiligenceCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("dd-analyst")
- await analyst_command(ctx, ticker)
+ await ShowView().discord(analyst_command, inter, "dd-analyst", ticker)
@commands.slash_command(name="dd-pt")
async def pt(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
raw: bool = False,
start="",
@@ -65,13 +47,11 @@ class DueDiligenceCommands(commands.Cog):
raw: True or false
start: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("dd-pt")
- await pt_command(ctx, ticker, raw, start)
+ await ShowView().discord(pt_command, inter, "dd-pt", ticker, raw, start)
@commands.slash_command(name="dd-est")
async def est(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays earning estimates [Business Insider]
@@ -80,13 +60,11 @@ class DueDiligenceCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("dd-est")
- await est_command(ctx, ticker)
+ await ShowView().discord(est_command, inter, "dd-est", ticker)
@commands.slash_command(name="dd-sec")
async def sec(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays sec filings [Market Watch]
@@ -95,13 +73,11 @@ class DueDiligenceCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("dd-sec")
- await sec_command(ctx, ticker)
+ await ShowView().discord(sec_command, inter, "dd-sec", ticker)
@commands.slash_command(name="dd-supplier")
async def supplier(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays suppliers of the company [CSIMarket]
@@ -110,13 +86,11 @@ class DueDiligenceCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("dd-supplier")
- await supplier_command(ctx, ticker)
+ await ShowView().discord(supplier_command, inter, "dd-supplier", ticker)
@commands.slash_command(name="dd-customer")
async def customer(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays customers of the company [CSIMarket]
@@ -125,13 +99,11 @@ class DueDiligenceCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("dd-customer")
- await customer_command(ctx, ticker)
+ await ShowView().discord(customer_command, inter, "dd-customer", ticker)
@commands.slash_command(name="dd-arktrades")
async def arktrades(
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
num: int = 10,
):
@@ -142,9 +114,7 @@ class DueDiligenceCommands(commands.Cog):
ticker: Stock Ticker
num: number of rows displayed
"""
- await ctx.response.defer()
- logger.info("dd-arktrades")
- await arktrades_command(ctx, ticker, num)
+ await ShowView().discord(arktrades_command, inter, "dd-arktrades", ticker, num)
def setup(bot: commands.Bot):
diff --git a/bots/cmds/dps_cmds.py b/bots/cmds/dps_cmds.py
new file mode 100644
index 00000000000..04f9988fa45
--- /dev/null
+++ b/bots/cmds/dps_cmds.py
@@ -0,0 +1,156 @@
+from __future__ import annotations
+
+import disnake
+from disnake.ext import commands
+
+from bots.helpers import ShowView, ticker_autocomp
+from bots.stocks.dark_pool_shorts.dpotc import dpotc_command
+from bots.stocks.dark_pool_shorts.ftd import ftd_command
+from bots.stocks.dark_pool_shorts.hsi import hsi_command
+from bots.stocks.dark_pool_shorts.pos import pos_command
+from bots.stocks.dark_pool_shorts.psi import psi_command
+from bots.stocks.dark_pool_shorts.shorted import shorted_command
+from bots.stocks.dark_pool_shorts.sidtc import sidtc_command
+from bots.stocks.dark_pool_shorts.spos import spos_command
+
+pos_choices = {
+ "Short Vol (1M)": "sv",
+ "Short Vol %": "sv_pct",
+ "Net Short Vol (1M)": "nsv",
+ "Net Short Vol ($100M)": "nsv_dollar",
+ "DP Position (1M)": "dpp",
+ "DP Position ($1B)": "dpp_dollar",
+}
+
+
+class DarkPoolShortsCommands(commands.Cog):
+ """Dark Pool Shorts menu"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+ self.bot.help_command.cog = self
+
+ def cog_unload(self):
+ self.bot.help_command = None
+
+ @commands.slash_command(name="dps-shorted")
+ async def shorted(self, inter: disnake.AppCmdInter, num: int = 10):
+ """Show most shorted stocks [Yahoo Finance]
+
+ Parameters
+ -----------
+ num: Number of the most shorted stocks to retrieve
+ """
+ await ShowView().discord(shorted_command, inter, "dps-shorted", num)
+
+ @commands.slash_command(name="dps-hsi")
+ async def hsi(self, inter: disnake.AppCmdInter, num: int = 10):
+ """Show top high short interest stocks of over 20% ratio [shortinterest.com]
+
+ Parameters
+ -----------
+ num: Number of top stocks to print
+ """
+ await ShowView().discord(hsi_command, inter, "dps-hsi", num)
+
+ @commands.slash_command(name="dps-pos")
+ async def pos(
+ self,
+ inter: disnake.AppCmdInter,
+ sort: str = commands.Param(choices=pos_choices),
+ num: int = 10,
+ ):
+ """Dark pool short position [Stockgrid]
+
+ Parameters
+ -----------
+ sort: Field for which to sort.
+ num: Number of top tickers to show
+ """
+ await ShowView().discord(pos_command, inter, "dps-pos", sort, num)
+
+ @commands.slash_command(name="dps-sidtc")
+ async def sidtc(
+ self,
+ inter: disnake.AppCmdInter,
+ sort: str = commands.Param(
+ choices={
+ "Float Short %": "float",
+ "Days to Cover": "dtc",
+ "Short Interest": "si",
+ }
+ ),
+ num: int = 10,
+ ):
+ """Short interest and days to cover [Stockgrid]
+
+ Parameters
+ -----------
+ sort: Field for which to sort. Possible are: `float`, `dtc`, `si`.
+ num: Number of top tickers to show
+ """
+ await ShowView().discord(sidtc_command, inter, "dps-sidtc", sort, num)
+
+ @commands.slash_command(name="dps-ftd")
+ async def ftd(
+ self,
+ inter: disnake.AppCmdInter,
+ ticker: str = commands.Param(autocomplete=ticker_autocomp),
+ start="",
+ end="",
+ ):
+ """Fails-to-deliver data [SEC]
+
+ Parameters
+ ----------
+ ticker: Stock Ticker
+ start: YYYY-MM-DD format
+ end: YYYY-MM-DD format
+ """
+ await ShowView().discord(ftd_command, inter, "dps-ftd", ticker, start, end)
+
+ @commands.slash_command(name="dps-dpotc")
+ async def dpotc(
+ self,
+ inter: disnake.AppCmdInter,
+ ticker: str = commands.Param(autocomplete=ticker_autocomp),
+ ):
+ """Dark pools (ATS) vs OTC data [FINRA]
+
+ Parameters
+ ----------
+ ticker: Stock Ticker
+ """
+ await ShowView().discord(dpotc_command, inter, "dps-dpotc", ticker)
+
+ @commands.slash_command(name="dps-spos")
+ async def spos(
+ self,
+ inter: disnake.AppCmdInter,
+ ticker: str = commands.Param(autocomplete=ticker_autocomp),
+ ):
+ """Net short vs position [Stockgrid]
+
+ Parameters
+ ----------
+ ticker: Stock Ticker
+ """
+ await ShowView().discord(spos_command, inter, "dps-spos", ticker)
+
+ @commands.slash_command(name="dps-psi")
+ async def psi(
+ self,
+ inter: disnake.AppCmdInter,
+ ticker: str = commands.Param(autocomplete=ticker_autocomp),
+ ):
+ """Price vs short interest volume [Stockgrid]
+
+ Parameters
+ ----------
+ ticker: Stock Ticker
+ """
+ await ShowView().discord(psi_command, inter, "dps-psi", ticker)
+
+
+def setup(bot: commands.Bot):
+ bot.add_cog(DarkPoolShortsCommands(bot))
diff --git a/bots/cmds/economy_cmds.py b/bots/cmds/economy_cmds.py
new file mode 100644
index 00000000000..e0fbb70a322
--- /dev/null
+++ b/bots/cmds/economy_cmds.py
@@ -0,0 +1,149 @@
+import disnake
+from disnake.ext import commands
+
+from bots.economy.currencies import currencies_command
+from bots.economy.energy import energy_command
+from bots.economy.feargreed import feargreed_command
+from bots.economy.futures import futures_command
+from bots.economy.glbonds import glbonds_command
+from bots.economy.grains import grains_command
+from bots.economy.indices import indices_command
+from bots.economy.meats import meats_command
+from bots.economy.metals import metals_command
+from bots.economy.overview import overview_command
+from bots.economy.performance import performance_command
+from bots.economy.softs import softs_command
+from bots.economy.usbonds import usbonds_command
+from bots.economy.valuation import valuation_command
+from bots.helpers import ShowView
+
+group = [
+ "basic_materials",
+ "capitalization",
+ "communication_services",
+ "consumer_cyclical",
+ "consumer_defensive",
+ "country",
+ "energy",
+ "financial",
+ "healthcare",
+ "industry",
+ "industrials",
+ "real_estate",
+ "sector",
+ "technology",
+ "utilities",
+]
+
+fgind = {
+ "Junk Bond Demand": "jbd",
+ "Market Volatility": "mv",
+ "Put and Call Options": "pco",
+ "Market Momentum": "mm",
+ "Stock Price Strength": "sps",
+ "Stock Price Breadth": "spb",
+ "Safe Heaven Demand": "shd",
+}
+
+
+class EconomyCommands(commands.Cog):
+ """Economy Commands menu"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+
+ @commands.slash_command(name="econ-feargreed")
+ async def feargreed(self, inter: disnake.AppCmdInter):
+ """Feargreed command [CNN]"""
+ await ShowView().discord(feargreed_command, inter, "econ-feargreed")
+
+ @commands.slash_command(name="econ-overview")
+ async def overview(self, inter: disnake.AppCmdInter):
+ """Market data overview [Wall St. Journal]"""
+ await ShowView().discord(overview_command, inter, "econ-overview")
+
+ @commands.slash_command(name="econ-indices")
+ async def indices(self, inter: disnake.AppCmdInter):
+ """US indices overview [Wall St. Journal]"""
+ await ShowView().discord(indices_command, inter, "econ-indices")
+
+ @commands.slash_command(name="econ-futures")
+ async def futures(self, inter: disnake.AppCmdInter):
+ """Futures and commodities overview [Wall St. Journal]"""
+ await ShowView().discord(futures_command, inter, "econ-futures")
+
+ @commands.slash_command(name="econ-usbonds")
+ async def usbonds(self, inter: disnake.AppCmdInter):
+ """US bonds overview [Wall St. Journal]"""
+ await ShowView().discord(usbonds_command, inter, "econ-usbonds")
+
+ @commands.slash_command(name="econ-glbonds")
+ async def glbonds(self, inter: disnake.AppCmdInter):
+ """Global bonds overview [Wall St. Journal]"""
+ await ShowView().discord(glbonds_command, inter, "econ-glbonds")
+
+ @commands.slash_command(name="econ-energy")
+ async def energy(self, inter: disnake.AppCmdInter):
+ """Displays energy futures data [Finviz]"""
+ await ShowView().discord(energy_command, inter, "econ-energy")
+
+ @commands.slash_command(name="econ-metals")
+ async def metals(self, inter: disnake.AppCmdInter):
+ """Displays metals futures data [Finviz]"""
+ await ShowView().discord(metals_command, inter, "econ-metals")
+
+ @commands.slash_command(name="econ-meats")
+ async def meats(self, inter: disnake.AppCmdInter):
+ """Displays meats futures data [Finviz]"""
+ await ShowView().discord(meats_command, inter, "econ-meats")
+
+ @commands.slash_command(name="econ-grains")
+ async def grains(self, inter: disnake.AppCmdInter):
+ """Displays grains futures data [Finviz]"""
+ await ShowView().discord(grains_command, inter, "econ-grains")
+
+ @commands.slash_command(name="econ-softs")
+ async def softs(self, inter: disnake.AppCmdInter):
+ """Displays softs futures data [Finviz]"""
+ await ShowView().discord(softs_command, inter, "econ-softs")
+
+ @commands.slash_command(name="econ-currencies")
+ async def currencies(self, inter: disnake.AppCmdInter):
+ """Currencies overview [Wall St. Journal]"""
+ await ShowView().discord(currencies_command, inter, "econ-currencies")
+
+ @commands.slash_command(name="econ-valuation")
+ async def valuation(
+ self,
+ inter: disnake.AppCmdInter,
+ economy_group: str = commands.Param(choices=group),
+ ):
+ """Valuation of sectors, industry, country [Finviz]
+
+ Parameters
+ -----------
+ economy_group: Economy Group
+ """
+ await ShowView().discord(
+ valuation_command, inter, "econ-valuation", economy_group
+ )
+
+ @commands.slash_command(name="econ-performance")
+ async def performance(
+ self,
+ inter: disnake.AppCmdInter,
+ economy_group: str = commands.Param(choices=group),
+ ):
+ """Performance of sectors, industry, country [Finviz]
+
+ Parameters
+ -----------
+ economy_group: Economy Group
+ """
+ await ShowView().discord(
+ performance_command, inter, "econ-performance", economy_group
+ )
+
+
+def setup(bot: commands.Bot):
+ bot.add_cog(EconomyCommands(bot))
diff --git a/discordbot/cmds/gov_cmds.py b/bots/cmds/gov_cmds.py
similarity index 61%
rename from discordbot/cmds/gov_cmds.py
rename to bots/cmds/gov_cmds.py
index 3acd17cdb23..1d81467e3a8 100644
--- a/discordbot/cmds/gov_cmds.py
+++ b/bots/cmds/gov_cmds.py
@@ -1,35 +1,19 @@
from __future__ import annotations
import disnake
-import pandas as pd
from disnake.ext import commands
-from discordbot.config_discordbot import logger
-from discordbot.stocks.government.contracts import contracts_command
-from discordbot.stocks.government.gtrades import gtrades_command
-from discordbot.stocks.government.histcont import histcont_command
-from discordbot.stocks.government.lastcontracts import lastcontracts_command
-from discordbot.stocks.government.lasttrades import lasttrades_command
-from discordbot.stocks.government.lobbying import lobbying_command
-from discordbot.stocks.government.qtrcontracts import qtrcontracts_command
-from discordbot.stocks.government.topbuys import topbuys_command
-from discordbot.stocks.government.toplobbying import toplobbying_command
-from discordbot.stocks.government.topsells import topsells_command
-
-
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "for a", "stock ticker"]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
+from bots.helpers import ShowView, ticker_autocomp
+from bots.stocks.government.contracts import contracts_command
+from bots.stocks.government.gtrades import gtrades_command
+from bots.stocks.government.histcont import histcont_command
+from bots.stocks.government.lastcontracts import lastcontracts_command
+from bots.stocks.government.lasttrades import lasttrades_command
+from bots.stocks.government.lobbying import lobbying_command
+from bots.stocks.government.qtrcontracts import qtrcontracts_command
+from bots.stocks.government.topbuys import topbuys_command
+from bots.stocks.government.toplobbying import toplobbying_command
+from bots.stocks.government.topsells import topsells_command
class GovernmentCommands(commands.Cog):
@@ -45,7 +29,7 @@ class GovernmentCommands(commands.Cog):
@commands.slash_command(name="gov-lasttrades")
async def lasttrades(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
gov_type: str = commands.Param(choices=["congress", "senate", "house"]),
past_days: int = 5,
representative="",
@@ -58,14 +42,19 @@ class GovernmentCommands(commands.Cog):
past_days: Positive number of past transaction days
representative: Enter name of a representative
"""
- await ctx.response.defer()
- logger.info("gov-lasttrades")
- await lasttrades_command(ctx, gov_type, past_days, representative)
+ await ShowView().discord(
+ lasttrades_command,
+ inter,
+ "gov-lasttrades",
+ gov_type,
+ past_days,
+ representative,
+ )
@commands.slash_command(name="gov-topbuys")
async def topbuys(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
gov_type: str = commands.Param(choices=["congress", "senate", "house"]),
past_transactions_months: int = 5,
num: int = 20,
@@ -80,14 +69,20 @@ class GovernmentCommands(commands.Cog):
num: Number of most sold stocks to retrieve
raw: If raw data should be outputted
"""
- await ctx.response.defer()
- logger.info("gov-topbuys")
- await topbuys_command(ctx, gov_type, past_transactions_months, num, raw)
+ await ShowView().discord(
+ topbuys_command,
+ inter,
+ "gov-topbuys",
+ gov_type,
+ past_transactions_months,
+ num,
+ raw,
+ )
@commands.slash_command(name="gov-topsells")
async def topsells(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
gov_type: str = commands.Param(choices=["congress", "senate", "house"]),
past_transactions_months: int = 5,
num: int = 10,
@@ -102,13 +97,19 @@ class GovernmentCommands(commands.Cog):
num: Number of most sold stocks to retrieve
raw: If raw data should be outputted
"""
- await ctx.response.defer()
- logger.info("gov-topsells")
- await topsells_command(ctx, gov_type, past_transactions_months, num, raw)
+ await ShowView().discord(
+ topsells_command,
+ inter,
+ "gov-topsells",
+ gov_type,
+ past_transactions_months,
+ num,
+ raw,
+ )
@commands.slash_command(name="gov-lastcontracts")
async def lastcontracts(
- self, ctx: disnake.AppCmdInter, past_transactions_days: int = 2, num: int = 20
+ self, inter: disnake.AppCmdInter, past_transactions_days: int = 2, num: int = 20
):
"""Displays last government contracts [quiverquant.com]
@@ -117,14 +118,18 @@ class GovernmentCommands(commands.Cog):
past_transactions_days: Positive number of past transaction days
num: Number of contracts
"""
- await ctx.response.defer()
- logger.info("gov-lastcontracts")
- await lastcontracts_command(ctx, past_transactions_days, num)
+ await ShowView().discord(
+ lastcontracts_command,
+ inter,
+ "gov-lastcontracts",
+ past_transactions_days,
+ num,
+ )
@commands.slash_command(name="gov-qtrcontracts")
async def qtrcontracts(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
num: int = 20,
analysis: str = commands.Param(choices=["total", "upmom", "downmom"]),
):
@@ -135,13 +140,13 @@ class GovernmentCommands(commands.Cog):
analysis: Possible arguments: total, upmom & downmom
num: Number of contracts
"""
- await ctx.response.defer()
- logger.info("gov-qtrcontracts")
- await qtrcontracts_command(ctx, num, analysis)
+ await ShowView().discord(
+ qtrcontracts_command, inter, "gov-qtrcontracts", num, analysis
+ )
@commands.slash_command(name="gov-toplobbying")
async def toplobbying(
- self, ctx: disnake.AppCmdInter, num: int = 10, raw: bool = False
+ self, inter: disnake.AppCmdInter, num: int = 10, raw: bool = False
):
"""Displays top lobbying firms [quiverquant.com]
@@ -150,14 +155,14 @@ class GovernmentCommands(commands.Cog):
num: Number to show
raw: If raw data should be outputted
"""
- await ctx.response.defer()
- logger.info("gov-toplobbying")
- await toplobbying_command(ctx, num, raw)
+ await ShowView().discord(
+ toplobbying_command, inter, "gov-toplobbying", num, raw
+ )
@commands.slash_command(name="gov-gtrades")
async def gtrades(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
gov_type: str = commands.Param(choices=["congress", "senate", "house"]),
past_transactions_months: int = 10,
@@ -172,14 +177,20 @@ class GovernmentCommands(commands.Cog):
past_transactions_months: Positive number of past transaction months
raw: If raw data should be outputted
"""
- await ctx.response.defer()
- logger.info("gov-gtrades")
- await gtrades_command(ctx, ticker, gov_type, past_transactions_months, raw)
+ await ShowView().discord(
+ gtrades_command,
+ inter,
+ "gov-gtrades",
+ ticker,
+ gov_type,
+ past_transactions_months,
+ raw,
+ )
@commands.slash_command(name="gov-contracts")
async def contracts(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
past_transaction_days: int = 10,
raw: bool = False,
@@ -192,14 +203,19 @@ class GovernmentCommands(commands.Cog):
past_transaction_days: Number of past transaction months
raw: If raw data should be outputted
"""
- await ctx.response.defer()
- logger.info("gov-contracts")
- await contracts_command(ctx, ticker, past_transaction_days, raw)
+ await ShowView().discord(
+ contracts_command,
+ inter,
+ "gov-contracts",
+ ticker,
+ past_transaction_days,
+ raw,
+ )
@commands.slash_command(name="gov-histcont")
async def histcont(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays historical quarterly-contracts [quiverquant.com]
@@ -208,14 +224,12 @@ class GovernmentCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("gov-histcont")
- await histcont_command(ctx, ticker)
+ await ShowView().discord(histcont_command, inter, "gov-histcont", ticker)
@commands.slash_command(name="gov-lobbying")
async def lobbying(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
num: int = 10,
):
@@ -226,9 +240,7 @@ class GovernmentCommands(commands.Cog):
ticker: Stock Ticker
num: Number of events
"""
- await ctx.response.defer()
- logger.info("gov-lobbying")
- await lobbying_command(ctx, ticker, num)
+ await ShowView().discord(lobbying_command, inter, "gov-lobbying", ticker, num)
def setup(bot: commands.Bot):
diff --git a/discordbot/cmds/options_cmds.py b/bots/cmds/options_cmds.py
similarity index 62%
rename from discordbot/cmds/options_cmds.py
rename to bots/cmds/options_cmds.py
index 5912075249e..95a36794881 100644
--- a/discordbot/cmds/options_cmds.py
+++ b/bots/cmds/options_cmds.py
@@ -1,74 +1,28 @@
from __future__ import annotations
-import pickle
-import time
-
import disnake
import disnake.ext.commands as commands
-import pandas as pd
-from discordbot.config_discordbot import logger
-from discordbot.stocks.candle import candle_command
-from discordbot.stocks.disc.ford import ford_command
-from discordbot.stocks.insider.lins import lins_command
-from discordbot.stocks.options.cc_hist import cc_hist_command
-from discordbot.stocks.options.hist import hist_command
-from discordbot.stocks.options.iv import iv_command
-from discordbot.stocks.options.oi import oi_command
-from discordbot.stocks.options.opt_chain import chain_command
-from discordbot.stocks.options.overview import overview_command
-from discordbot.stocks.options.unu import unu_command
-from discordbot.stocks.options.vol import vol_command
-from discordbot.stocks.options.vsurf import vsurf_command
-from gamestonk_terminal.stocks.options import yfinance_model
+from bots.helpers import ShowView, expiry_autocomp, ticker_autocomp
+from bots.stocks.candle import candle_command
+from bots.stocks.disc.ford import ford_command
+from bots.stocks.insider.lins import lins_command
+from bots.stocks.options.cc_hist import cc_hist_command
+from bots.stocks.options.hist import hist_command
+from bots.stocks.options.iv import iv_command
+from bots.stocks.options.oi import oi_command
+from bots.stocks.options.opt_chain import chain_command
+from bots.stocks.options.overview import overview_command
+from bots.stocks.options.unu import unu_command
+from bots.stocks.options.vol import vol_command
+from bots.stocks.options.vsurf import vsurf_command
+from bots.stocks.quote import quote_command
-startTime = time.time()
-data = []
-
-
-class Ticker:
- def __init__(self, ticker):
- super().__init__()
- global tickerr
- self._ticker = ticker
- tickerr = ticker
-
- @property
- def ticker(self):
- return self._ticker
-
- def __repr__(self) -> str:
- data.append(f"{self._ticker}")
- file = open("ticker", "wb")
- pickle.dump(data, file)
- file.close()
- return f"{self._ticker}"
-
-
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "for a", "stock ticker"]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- if ticker:
- ticker = str(Ticker(ticker))
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
-
-
-def expiry_autocomp(inter: disnake.AppCmdInter, tickerr: str):
- file = open("ticker", "rb")
- data = pickle.load(file)
- file.close()
- print(data)
- dates = yfinance_model.option_expirations(data[-1])
- return [dates for dates in dates][:24]
+vsurf_choices = {
+ "Volatility": "IV",
+ "Open Interest": "OI",
+ "Last Price": "LP",
+}
class SlashCommands(commands.Cog):
@@ -96,9 +50,9 @@ class SlashCommands(commands.Cog):
min_sp: Minimum Strike Price
max_sp: Maximum Strike Price
"""
- await inter.response.defer()
- logger.info("opt-chain")
- await chain_command(inter, ticker, expiry, opt_type, min_sp, max_sp)
+ await ShowView().discord(
+ chain_command, inter, "opt-chain", ticker, expiry, opt_type, min_sp, max_sp
+ )
@commands.slash_command(name="opt-oi")
async def open_interest(
@@ -118,9 +72,9 @@ class SlashCommands(commands.Cog):
min_sp: Minimum Strike Price
max_sp: Maximum Strike Price
"""
- await inter.response.defer()
- logger.info("opt-oi")
- await oi_command(inter, ticker, expiry, min_sp, max_sp)
+ await ShowView().discord(
+ oi_command, inter, "opt-oi", ticker, expiry, min_sp, max_sp
+ )
@commands.slash_command(name="opt-iv")
async def iv(
@@ -134,9 +88,21 @@ class SlashCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await inter.response.defer()
- logger.info("opt-iv")
- await iv_command(inter, ticker)
+ await ShowView().discord(iv_command, inter, "opt-iv", ticker)
+
+ @commands.slash_command(name="q")
+ async def quote(
+ self,
+ inter: disnake.AppCmdInter,
+ ticker: str = commands.Param(autocomplete=ticker_autocomp),
+ ):
+ """Displays ticker quote [yFinance]
+
+ Parameters
+ -----------
+ ticker: Stock Ticker
+ """
+ await ShowView().discord(quote_command, inter, "quote", ticker)
@commands.slash_command(name="disc-ford")
async def ford(self, inter: disnake.AppCmdInter):
@@ -146,16 +112,12 @@ class SlashCommands(commands.Cog):
-----------
num: Number of stocks to display
"""
- await inter.response.defer()
- logger.info("disc-ford")
- await ford_command(inter)
+ await ShowView().discord(ford_command, inter, "disc-ford")
@commands.slash_command(name="opt-unu")
async def unu(self, inter: disnake.AppCmdInter):
"""Unusual Options"""
- await inter.response.defer()
- logger.info("disc-ford")
- await unu_command(inter)
+ await ShowView().discord(unu_command, inter, "disc-ford")
@commands.slash_command(name="ins-last")
async def lins(
@@ -171,9 +133,7 @@ class SlashCommands(commands.Cog):
ticker : Stock Ticker
num : Number of latest insider activity to display
"""
- await inter.response.defer()
- logger.info("ins-last")
- await lins_command(inter, ticker, num)
+ await lins_command(inter, "ins-last", ticker, num)
@commands.slash_command(name="cc")
async def cc(
@@ -195,9 +155,9 @@ class SlashCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await inter.response.defer()
- logger.info("cc")
- await candle_command(inter, ticker, interval, past_days, start, end)
+ await ShowView().discord(
+ candle_command, inter, "cc", ticker, interval, past_days, start, end
+ )
@commands.slash_command(name="btc")
async def btc(
@@ -217,9 +177,9 @@ class SlashCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await inter.response.defer()
- logger.info("btc")
- await candle_command(inter, "btc-usd", interval, past_days, start, end)
+ await ShowView().discord(
+ candle_command, inter, "btc", "btc-usd", interval, past_days, start, end
+ )
@commands.slash_command(name="eth")
async def eth(
@@ -239,9 +199,9 @@ class SlashCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await inter.response.defer()
- logger.info("eth")
- await candle_command(inter, "eth-usd", interval, past_days, start, end)
+ await ShowView().discord(
+ candle_command, inter, "eth", "eth-usd", interval, past_days, start, end
+ )
@commands.slash_command(name="sol")
async def sol(
@@ -261,9 +221,9 @@ class SlashCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await inter.response.defer()
- logger.info("sol")
- await candle_command(inter, "sol-usd", interval, past_days, start, end)
+ await ShowView().discord(
+ candle_command, inter, "sol", "sol-usd", interval, past_days, start, end
+ )
@commands.slash_command(name="opt-overview")
async def overview(
@@ -283,9 +243,9 @@ class SlashCommands(commands.Cog):
min_sp: Minimum Strike Price
max_sp: Maximum Strike Price
"""
- await inter.response.defer()
- logger.info("opt-overview")
- await overview_command(inter, ticker, expiry, min_sp, max_sp)
+ await ShowView().discord(
+ overview_command, inter, "opt-overview", ticker, expiry, min_sp, max_sp
+ )
@commands.slash_command(name="opt-vol")
async def volume(
@@ -301,16 +261,14 @@ class SlashCommands(commands.Cog):
ticker: Stock Ticker
expiry: Expiration Date
"""
- await inter.response.defer()
- logger.info("opt-vol")
- await vol_command(inter, ticker, expiry)
+ await ShowView().discord(vol_command, inter, "opt-vol", ticker, expiry)
@commands.slash_command(name="opt-vsurf")
async def vsurf(
self,
inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
- z: str = commands.Param(choices=["Volatility", "Open Interest", "Last Price"]),
+ z: str = commands.Param(choices=vsurf_choices),
):
"""Display Volatility Surface
@@ -319,17 +277,7 @@ class SlashCommands(commands.Cog):
ticker: Stock Ticker
z: The variable for the Z axis
"""
- await inter.response.defer()
- logger.info("opt-vsurf")
-
- if z == "Volatility":
- z = "IV"
- if z == "Open Interest":
- z = "OI"
- if z == "Last Price":
- z = "LP"
-
- await vsurf_command(inter, ticker, z)
+ await ShowView().discord(vsurf_command, inter, "opt-vsurf", ticker, z)
@commands.slash_command(name="opt-hist")
async def history(
@@ -358,12 +306,12 @@ class SlashCommands(commands.Cog):
ticker: Stock Ticker
expiry: Expiration Date
strike: Options Strike Price
- type: Calls or Puts
+ opt_type: Calls or Puts
greek: Greek variable to plot
"""
- await inter.response.defer()
- logger.info("opt-hist")
- await hist_command(inter, ticker, expiry, strike, opt_type, greek)
+ await ShowView().discord(
+ hist_command, inter, "opt-hist", ticker, expiry, strike, opt_type, greek
+ )
@commands.slash_command(name="opt-cc-hist")
async def cc_history(
@@ -381,14 +329,12 @@ class SlashCommands(commands.Cog):
ticker: Stock Ticker
expiry: Expiration Date
strike: Options Strike Price
- type: Calls or Puts
+ opt_type: Calls or Puts
"""
- await inter.response.defer()
- logger.info("opt-cc-hist")
- await cc_hist_command(inter, ticker, expiry, strike, opt_type)
+ await ShowView().discord(
+ cc_hist_command, inter, "opt-cc-hist", ticker, expiry, strike, opt_type
+ )
def setup(bot):
bot.add_cog(SlashCommands(bot))
- executionTime = time.time() - startTime
- print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/cmds/screener_cmds.py b/bots/cmds/screener_cmds.py
similarity index 56%
rename from discordbot/cmds/screener_cmds.py
rename to bots/cmds/screener_cmds.py
index 72120815631..21f2ff0edad 100644
--- a/discordbot/cmds/screener_cmds.py
+++ b/bots/cmds/screener_cmds.py
@@ -1,96 +1,19 @@
from __future__ import annotations
import disnake
-import pandas as pd
from disnake.ext import commands
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener.financial import financial_command
-from discordbot.stocks.screener.historical import historical_command
-from discordbot.stocks.screener.overview import overview_command
-from discordbot.stocks.screener.ownership import ownership_command
-from discordbot.stocks.screener.performance import performance_command
-from discordbot.stocks.screener.presets_custom import presets_custom_command
-from discordbot.stocks.screener.presets_default import presets_default_command
-from discordbot.stocks.screener.technical import technical_command
-from discordbot.stocks.screener.valuation import valuation_command
+from bots.helpers import ShowView, presets_custom_autocomp, signals_autocomp
+from bots.stocks.screener.financial import financial_command
+from bots.stocks.screener.historical import historical_command
+from bots.stocks.screener.overview import overview_command
+from bots.stocks.screener.ownership import ownership_command
+from bots.stocks.screener.performance import performance_command
+from bots.stocks.screener.presets_custom import presets_custom_command
+from bots.stocks.screener.presets_default import presets_default_command
+from bots.stocks.screener.technical import technical_command
+from bots.stocks.screener.valuation import valuation_command
-presets_custom = [
- "potential_reversals",
- "golden_cross_penny",
- "rosenwald_gtfo",
- "golden_cross",
- "bull_runs_over_10pct",
- "recent_growth_and_support",
- "heavy_inst_ins",
- "short_squeeze_scan",
- "under_15dol_stocks",
- "top_performers_healthcare",
- "oversold_under_3dol",
- "value_stocks",
- "cheap_dividend",
- "death_cross",
- "top_performers_tech",
- "unusual_volume",
- "cheap_oversold",
- "undervalue",
- "high_vol_and_low_debt",
- "simplistic_momentum_scanner_under_7dol",
- "5pct_above_low",
- "growth_stocks",
- "cheap_bottom_dividend",
- "analyst_strong_buy",
- "oversold",
- "rosenwald",
- "weak_support_and_top_performers",
- "channel_up_and_low_debt_and_sma_50and200",
- "template",
- "modified_neff",
- "buffett_like",
- "oversold_under_5dol",
- "sexy_year",
- "news_scanner",
- "top_performers_all",
- "stocks_strong_support_levels",
- "continued_momentum_scan",
- "modified_dreman",
- "break_out_stocks",
-]
-signals = [
- "top_gainers",
- "top_losers",
- "new_high",
- "new_low",
- "most_volatile",
- "most_active",
- "unusual_volume",
- "overbought",
- "oversold",
- "downgrades",
- "upgrades",
- "earnings_before",
- "earnings_after",
- "recent_insider_buying",
- "recent_insider_selling",
- "major_news",
- "horizontal_sr",
- "tl_resistance",
- "tl_support",
- "wedge_up",
- "wedge_down",
- "wedge",
- "triangle_ascending",
- "triangle_descending",
- "channel_up",
- "channel_down",
- "channel",
- "double_top",
- "double_bottom",
- "multiple_top",
- "multiple_bottom",
- "head_shoulders",
- "head_shoulders_inverse",
-]
sort = {
"overview": [
"Ticker",
@@ -195,39 +118,6 @@ sort = {
# pylint: disable=R0912
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "for a", "stock ticker"]
-
-
-def presets_custom_autocomp(inter: disnake.AppCmdInter, preset: str):
- df = presets_custom
- if not preset:
- return df[:24]
- plow = preset.lower()
- print(f"preset_custom_autocomp [preset]: {preset}")
- return [preset for preset in df if preset.lower().startswith(plow)][:24]
-
-
-def signals_autocomp(inter: disnake.AppCmdInter, signal: str):
- df = signals
- if not signal:
- return df[:24]
- print(f"signal_autocomp [signal]: {signal}")
- slow = signal.lower()
- return [signal for signal in df if signal.lower().startswith(slow)][:24]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
-
-
class ScreenerCommands(commands.Cog):
"""Screener menu"""
@@ -235,21 +125,19 @@ class ScreenerCommands(commands.Cog):
self.bot = bot
@commands.slash_command(name="scr-presets_default")
- async def presets_default(self, ctx: disnake.AppCmdInter):
+ async def presets_default(self, inter: disnake.AppCmdInter):
"""Displays every available preset"""
- logger.info("scr-presets_default")
- await presets_default_command(ctx)
+ await ShowView().discord(presets_default_command, inter, "scr-presets_default")
@commands.slash_command(name="scr-presets_custom")
- async def presets_custom(self, ctx: disnake.AppCmdInter):
+ async def presets_custom(self, inter: disnake.AppCmdInter):
"""Displays every available preset"""
- logger.info("scr-presets_custom")
- await presets_custom_command(ctx)
+ await ShowView().discord(presets_custom_command, inter, "scr-presets_custom")
@commands.slash_command(name="scr-historical")
async def historical(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
signal: str = commands.Param(autocomplete=signals_autocomp),
start="",
):
@@ -260,14 +148,14 @@ class ScreenerCommands(commands.Cog):
signal: Signal. Default: most_volatile
start: Starting date in YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("scr-historical")
- await historical_command(ctx, signal, start)
+ await ShowView().discord(
+ historical_command, inter, "scr-historical", signal, start
+ )
@commands.slash_command(name="scr-overview")
async def overview(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["overview"]),
limit: int = 5,
@@ -282,14 +170,14 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-overview")
- await overview_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ overview_command, inter, "scr-overview", preset, sort, limit, ascend
+ )
@commands.slash_command(name="scr-valuation")
async def valuation(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["valuation"]),
limit: int = 5,
@@ -304,14 +192,14 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-valuation")
- await valuation_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ valuation_command, inter, "scr-valuation", preset, sort, limit, ascend
+ )
@commands.slash_command(name="scr-financial")
async def financial(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["financial"]),
limit: int = 5,
@@ -326,14 +214,14 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-financial")
- await financial_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ financial_command, inter, "scr-financial", preset, sort, limit, ascend
+ )
@commands.slash_command(name="scr-ownership")
async def ownership(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["ownership"]),
limit: int = 5,
@@ -348,14 +236,14 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-ownership")
- await ownership_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ ownership_command, inter, "scr-ownership", preset, sort, limit, ascend
+ )
@commands.slash_command(name="scr-performance")
async def performance(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["performance"]),
limit: int = 5,
@@ -370,14 +258,14 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-performance")
- await performance_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ performance_command, inter, "scr-performance", preset, sort, limit, ascend
+ )
@commands.slash_command(name="scr-technical")
async def technical(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
preset: str = commands.Param(autocomplete=presets_custom_autocomp),
sort: str = commands.Param(choices=sort["technical"]),
limit: int = 5,
@@ -392,9 +280,9 @@ class ScreenerCommands(commands.Cog):
limit: number of stocks to display
ascend: whether it's sorted by ascending order or not. Default: False
"""
- await ctx.response.defer()
- logger.info("scr-technical")
- await technical_command(ctx, preset, sort, limit, ascend)
+ await ShowView().discord(
+ technical_command, inter, "scr-technical", preset, sort, limit, ascend
+ )
def setup(bot: commands.Bot):
diff --git a/discordbot/cmds/ta_cmds.py b/bots/cmds/ta_cmds.py
similarity index 67%
rename from discordbot/cmds/ta_cmds.py
rename to bots/cmds/ta_cmds.py
index 6e45750738a..ed96782d9b7 100644
--- a/discordbot/cmds/ta_cmds.py
+++ b/bots/cmds/ta_cmds.py
@@ -2,33 +2,32 @@
from __future__ import annotations
import disnake
-import pandas as pd
from disnake.ext import commands
-from discordbot.config_discordbot import logger
-from discordbot.stocks.technical_analysis.ad import ad_command
-from discordbot.stocks.technical_analysis.adosc import adosc_command
-from discordbot.stocks.technical_analysis.adx import adx_command
-from discordbot.stocks.technical_analysis.aroon import aroon_command
-from discordbot.stocks.technical_analysis.bbands import bbands_command
-from discordbot.stocks.technical_analysis.cci import cci_command
-from discordbot.stocks.technical_analysis.cg import cg_command
-from discordbot.stocks.technical_analysis.donchian import donchian_command
-from discordbot.stocks.technical_analysis.ema import ema_command
-from discordbot.stocks.technical_analysis.fib import fib_command
-from discordbot.stocks.technical_analysis.fisher import fisher_command
-from discordbot.stocks.technical_analysis.hma import hma_command
-from discordbot.stocks.technical_analysis.kc import kc_command
-from discordbot.stocks.technical_analysis.macd import macd_command
-from discordbot.stocks.technical_analysis.obv import obv_command
-from discordbot.stocks.technical_analysis.recom import recom_command
-from discordbot.stocks.technical_analysis.rsi import rsi_command
-from discordbot.stocks.technical_analysis.sma import sma_command
-from discordbot.stocks.technical_analysis.stoch import stoch_command
-from discordbot.stocks.technical_analysis.summary import summary_command
-from discordbot.stocks.technical_analysis.view import view_command
-from discordbot.stocks.technical_analysis.wma import wma_command
-from discordbot.stocks.technical_analysis.zlma import zlma_command
+from bots.helpers import ShowView, ticker_autocomp
+from bots.stocks.technical_analysis.ad import ad_command
+from bots.stocks.technical_analysis.adosc import adosc_command
+from bots.stocks.technical_analysis.adx import adx_command
+from bots.stocks.technical_analysis.aroon import aroon_command
+from bots.stocks.technical_analysis.bbands import bbands_command
+from bots.stocks.technical_analysis.cci import cci_command
+from bots.stocks.technical_analysis.cg import cg_command
+from bots.stocks.technical_analysis.donchian import donchian_command
+from bots.stocks.technical_analysis.ema import ema_command
+from bots.stocks.technical_analysis.fib import fib_command
+from bots.stocks.technical_analysis.fisher import fisher_command
+from bots.stocks.technical_analysis.hma import hma_command
+from bots.stocks.technical_analysis.kc import kc_command
+from bots.stocks.technical_analysis.macd import macd_command
+from bots.stocks.technical_analysis.obv import obv_command
+from bots.stocks.technical_analysis.recom import recom_command
+from bots.stocks.technical_analysis.rsi import rsi_command
+from bots.stocks.technical_analysis.sma import sma_command
+from bots.stocks.technical_analysis.stoch import stoch_command
+from bots.stocks.technical_analysis.summary import summary_command
+from bots.stocks.technical_analysis.view import view_command
+from bots.stocks.technical_analysis.wma import wma_command
+from bots.stocks.technical_analysis.zlma import zlma_command
possible_ma = [
"dema",
@@ -51,21 +50,6 @@ possible_ma = [
]
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "for a", "stock ticker"]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
-
-
class TechnicalAnalysisCommands(commands.Cog):
"""Technical Analysis menu."""
@@ -75,7 +59,7 @@ class TechnicalAnalysisCommands(commands.Cog):
@commands.slash_command(name="ta-ema")
async def ema(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
window="",
offset: int = 0,
@@ -92,14 +76,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-ema")
- await ema_command(ctx, ticker, window, offset, start, end)
+ await ShowView().discord(
+ ema_command, inter, "ta-ema", ticker, window, offset, start, end
+ )
@commands.slash_command(name="ta-sma")
async def sma(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
window="",
offset: int = 0,
@@ -116,14 +100,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-sma")
- await sma_command(ctx, ticker, window, offset, start, end)
+ await ShowView().discord(
+ sma_command, inter, "ta-sma", ticker, window, offset, start, end
+ )
@commands.slash_command(name="ta-wma")
async def wma(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
window="",
offset: int = 0,
@@ -140,14 +124,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-wma")
- await wma_command(ctx, ticker, window, offset, start, end)
+ await ShowView().discord(
+ wma_command, inter, "ta-wma", ticker, window, offset, start, end
+ )
@commands.slash_command(name="ta-hma")
async def hma(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
window="",
offset: int = 0,
@@ -164,14 +148,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-hma")
- await hma_command(ctx, ticker, window, offset, start, end)
+ await ShowView().discord(
+ hma_command, inter, "ta-hma", ticker, window, offset, start, end
+ )
@commands.slash_command(name="ta-zlma")
async def zlma(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
window="",
offset: int = 0,
@@ -188,14 +172,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-zlma")
- await zlma_command(ctx, ticker, window, offset, start, end)
+ await ShowView().discord(
+ zlma_command, inter, "ta-zlma", ticker, window, offset, start, end
+ )
@commands.slash_command(name="ta-cci")
async def cci(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="14",
scalar="0.015",
@@ -212,14 +196,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-cci")
- await cci_command(ctx, ticker, length, scalar, start, end)
+ await ShowView().discord(
+ cci_command, inter, "ta-cci", ticker, length, scalar, start, end
+ )
@commands.slash_command(name="ta-macd")
async def macd(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
fast="12",
slow="26",
@@ -238,14 +222,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-macd")
- await macd_command(ctx, ticker, fast, slow, signal, start, end)
+ await ShowView().discord(
+ macd_command, inter, "ta-macd", ticker, fast, slow, signal, start, end
+ )
@commands.slash_command(name="ta-rsi")
async def rsi(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="14",
scalar="100",
@@ -264,14 +248,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-rsi")
- await rsi_command(ctx, ticker, length, scalar, drift, start, end)
+ await ShowView().discord(
+ rsi_command, inter, "ta-rsi", ticker, length, scalar, drift, start, end
+ )
@commands.slash_command(name="ta-stoch")
async def stoch(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
fast_k="14",
slow_d="3",
@@ -290,14 +274,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-stoch")
- await stoch_command(ctx, ticker, fast_k, slow_d, slow_k, start, end)
+ await ShowView().discord(
+ stoch_command, inter, "ta-stoch", ticker, fast_k, slow_d, slow_k, start, end
+ )
@commands.slash_command(name="ta-fisher")
async def fisher(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="14",
start="",
@@ -312,14 +296,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-fisher")
- await fisher_command(ctx, ticker, length, start, end)
+ await ShowView().discord(
+ fisher_command, inter, "ta-fisher", ticker, length, start, end
+ )
@commands.slash_command(name="ta-cg")
async def cg(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="14",
start="",
@@ -334,14 +318,12 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-cg")
- await cg_command(ctx, ticker, length, start, end)
+ await ShowView().discord(cg_command, inter, "ta-cg", ticker, length, start, end)
@commands.slash_command(name="ta-adx")
async def adx(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="14",
scalar="100",
@@ -360,14 +342,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-adx")
- await adx_command(ctx, ticker, length, scalar, drift, start, end)
+ await ShowView().discord(
+ adx_command, inter, "ta-adx", ticker, length, scalar, drift, start, end
+ )
@commands.slash_command(name="ta-aroon")
async def aroon(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="25",
scalar="100",
@@ -384,14 +366,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-aroon")
- await aroon_command(ctx, ticker, length, scalar, start, end)
+ await ShowView().discord(
+ aroon_command, inter, "ta-aroon", ticker, length, scalar, start, end
+ )
@commands.slash_command(name="ta-bbands")
async def bbands(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="5",
std="2",
@@ -410,15 +392,15 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-bbands")
ma_mode = str(ma_mode)
- await bbands_command(ctx, ticker, length, std, ma_mode, start, end)
+ await ShowView().discord(
+ bbands_command, inter, "ta-bbands", ticker, length, std, ma_mode, start, end
+ )
@commands.slash_command(name="ta-donchian")
async def donchian(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
upper_length="25",
lower_length="100",
@@ -435,15 +417,22 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-donchian")
- await donchian_command(ctx, ticker, upper_length, lower_length, start, end)
+ await ShowView().discord(
+ donchian_command,
+ inter,
+ "ta-donchian",
+ ticker,
+ upper_length,
+ lower_length,
+ start,
+ end,
+ )
# pylint: disable=too-many-arguments
@commands.slash_command(name="ta-kc")
async def kc(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
length="20",
scalar="2",
@@ -464,15 +453,24 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-kc")
ma_mode = str(ma_mode)
- await kc_command(ctx, ticker, length, scalar, ma_mode, offset, start, end)
+ await ShowView().discord(
+ kc_command,
+ inter,
+ "ta-kc",
+ ticker,
+ length,
+ scalar,
+ ma_mode,
+ offset,
+ start,
+ end,
+ )
@commands.slash_command(name="ta-ad")
async def ad(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
is_open="False",
start="",
@@ -487,14 +485,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-ad")
- await ad_command(ctx, ticker, is_open, start, end)
+ await ShowView().discord(
+ ad_command, inter, "ta-ad", ticker, is_open, start, end
+ )
@commands.slash_command(name="ta-adosc")
async def adosc(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
is_open="False",
fast="3",
@@ -513,14 +511,14 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-adosc")
- await adosc_command(ctx, ticker, is_open, fast, slow, start, end)
+ await ShowView().discord(
+ adosc_command, inter, "ta-adosc", ticker, is_open, fast, slow, start, end
+ )
@commands.slash_command(name="ta-obv")
async def obv(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
start="",
end="",
@@ -533,14 +531,12 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD
end: YYYY-MM-DD
"""
- await ctx.response.defer()
- logger.info("ta-obv")
- await obv_command(ctx, ticker, start, end)
+ await ShowView().discord(obv_command, inter, "ta-obv", ticker, start, end)
@commands.slash_command(name="ta-fib")
async def fib(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
start="",
end="",
@@ -553,14 +549,12 @@ class TechnicalAnalysisCommands(commands.Cog):
start: YYYY-MM-DD format
end: YYYY-MM-DD format
"""
- await ctx.response.defer()
- logger.info("ta-fib")
- await fib_command(ctx, ticker, start, end)
+ await ShowView().discord(fib_command, inter, "ta-fib", ticker, start, end)
@commands.slash_command(name="ta-view")
async def view(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays image from Finviz [Finviz]
@@ -569,14 +563,12 @@ class TechnicalAnalysisCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("ta-view")
- await view_command(ctx, ticker)
+ await ShowView().discord(view_command, inter, "ta-view", ticker)
@commands.slash_command(name="ta-summary")
async def summary(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays text of a given stocks ta summary [FinBrain API]
@@ -585,14 +577,12 @@ class TechnicalAnalysisCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("ta-summary")
- await summary_command(ctx, ticker)
+ await ShowView().discord(summary_command, inter, "ta-summary", ticker)
@commands.slash_command(name="ta-recom")
async def recom(
self,
- ctx: disnake.AppCmdInter,
+ inter: disnake.AppCmdInter,
ticker: str = commands.Param(autocomplete=ticker_autocomp),
):
"""Displays text of a given stocks recommendation based on ta [Tradingview API]
@@ -601,9 +591,7 @@ class TechnicalAnalysisCommands(commands.Cog):
-----------
ticker: Stock Ticker
"""
- await ctx.response.defer()
- logger.info("ta-recom")
- await recom_command(ctx, ticker)
+ await ShowView().discord(recom_command, inter, "ta-recom", ticker)
def setup(bot: commands.Bot):
diff --git a/discordbot/config_discordbot.py b/bots/config_discordbot.py
similarity index 96%
rename from discordbot/config_discordbot.py
rename to bots/config_discordbot.py
index 7bc5b3aa42b..a26e222468c 100644
--- a/discordbot/config_discordbot.py
+++ b/bots/config_discordbot.py
@@ -3,6 +3,7 @@ import logging
import os
import sys
+
import disnake
import pyimgur
@@ -31,7 +32,7 @@ DISCORD_BOT_TOKEN = os.getenv("GT_DISCORD_BOT_TOKEN") or "REPLACE_ME"
IMGUR_CLIENT_ID = os.getenv("GT_IMGUR_CLIENT_ID") or "REPLACE_ME"
# Settings
-SLASH_TESTING_SERVERS: List[int] = [] # Test server IDs
+SLASH_TESTING_SERVERS: List[int] = [936333632317046844] # Test server IDs
COMMAND_PREFIX = "!"
DATE_FORMAT = "%Y-%m-%d"
COLOR = disnake.Color.from_rgb(255, 0, 0)
diff --git a/discordbot/economy/__init__.py b/bots/economy/__init__.py
similarity index 100%
rename from discordbot/economy/__init__.py
rename to bots/economy/__init__.py
diff --git a/bots/economy/currencies.py b/bots/economy/currencies.py
new file mode 100644
index 00000000000..23287fae4bd
--- /dev/null
+++ b/bots/economy/currencies.py
@@ -0,0 +1,67 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def currencies_command():
+ """Currencies overview [Wall St. Journal]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-currencies")
+
+ # Retrieve data
+ df = wsj_model.global_currencies()
+ df = pd.DataFrame.from_dict(df)
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Last"] = pd.to_numeric(df["Last"].astype(float))
+ df["Chng"] = pd.to_numeric(df["Chng"].astype(float))
+ df["%Chng"] = pd.to_numeric(df["%Chng"].astype(float))
+
+ formats = {"Last": "{:.2f}", "Chng": "{:.2f}", "%Chng": "{:.2f}%"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.fillna("")
+ df.set_index(" ", inplace=True)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ df = df[
+ [
+ "Last",
+ "Chng",
+ "%Chng",
+ ]
+ ]
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-currencies.png", fig)
+ return {
+ "title": "Economy: [WSJ] Currencies",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/energy.py b/bots/economy/energy.py
new file mode 100644
index 00000000000..66caf3d575e
--- /dev/null
+++ b/bots/economy/energy.py
@@ -0,0 +1,70 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def energy_command():
+ """Displays energy futures data [Finviz]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-energy")
+
+ # Retrieve data
+ d_futures = finviz_model.get_futures()
+ df = pd.DataFrame(d_futures["Energy"])
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.sort_values(by="ticker", ascending=False)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ df = df.fillna("")
+ df.set_index("label", inplace=True)
+
+ df = df[
+ [
+ "prevClose",
+ "last",
+ "change",
+ ]
+ ]
+ df.index.names = [""]
+ df = df.rename(
+ columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
+ )
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 2],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-energy.png", fig)
+ return {
+ "title": "[Finviz] Energy Futures",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/feargreed.py b/bots/economy/feargreed.py
new file mode 100644
index 00000000000..484f23b0395
--- /dev/null
+++ b/bots/economy/feargreed.py
@@ -0,0 +1,57 @@
+import datetime
+import os
+
+import matplotlib.pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.economy import cnn_model, cnn_view
+
+
+def feargreed_command(indicator=""):
+ """CNN Fear and Greed Index [CNN]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-futures")
+
+ # Check for argument
+ possible_indicators = ("", "jbd", "mv", "pco", "mm", "sps", "spb", "shd")
+
+ if indicator not in possible_indicators:
+ raise Exception(
+ f"Select a valid indicator from {', '.join(possible_indicators)}" # nosec
+ )
+
+ # Retrieve data
+ fig = plt.figure(figsize=[1, 1], dpi=10)
+
+ report, _ = cnn_model.get_feargreed_report(indicator, fig)
+ cnn_view.fear_and_greed_index(indicator=indicator, export="png")
+ plt.close("all")
+
+ # Output data
+ now = datetime.datetime.now()
+ image_path = os.path.join(
+ cfg.GST_PATH,
+ "exports",
+ "economy",
+ f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png",
+ )
+
+ i = 0
+ while not os.path.exists(image_path) and i < 10:
+ now -= datetime.timedelta(seconds=1)
+ image_path = os.path.join(
+ cfg.GST_PATH,
+ "exports",
+ "economy",
+ f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png",
+ )
+ i += 1
+
+ return {
+ "title": "Economy: [CNN] Fear Geed Index",
+ "imagefile": image_path,
+ "description": report,
+ }
diff --git a/bots/economy/futures.py b/bots/economy/futures.py
new file mode 100644
index 00000000000..d8dcaf40bd0
--- /dev/null
+++ b/bots/economy/futures.py
@@ -0,0 +1,63 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def futures_command():
+ """Futures and commodities overview [Wall St. Journal]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-futures")
+
+ # Retrieve data
+ df = wsj_model.top_commodities()
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Price"] = pd.to_numeric(df["Price"].astype(float))
+ df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
+ df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.fillna("")
+ df.set_index(" ", inplace=True)
+
+ df = df[
+ [
+ "Price",
+ "Chg",
+ "%Chg",
+ ]
+ ]
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-futures.png", fig)
+ return {"title": "Economy: [WSJ] Futures/Commodities", "imagefile": imagefile}
diff --git a/bots/economy/glbonds.py b/bots/economy/glbonds.py
new file mode 100644
index 00000000000..c911c153297
--- /dev/null
+++ b/bots/economy/glbonds.py
@@ -0,0 +1,69 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def glbonds_command():
+ """Global bonds overview [Wall St. Journal]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-glbonds")
+
+ # Retrieve data
+ df = wsj_model.global_bonds()
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Rate (%)"] = pd.to_numeric(df["Rate (%)"].astype(float))
+ df["Yld (%)"] = pd.to_numeric(df["Yld (%)"].astype(float))
+ df["Yld Chg (%)"] = pd.to_numeric(df["Yld Chg (%)"].astype(float))
+
+ formats = {
+ "Rate (%)": "{:.2f}%",
+ "Yld (%)": "{:.2f}%",
+ "Yld Chg (%)": "{:.2f}%",
+ }
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ df = df.fillna("")
+ df = df.replace(("Government Bond", "10 Year"), ("Gov .Bond", "10yr"), regex=True)
+ df.set_index(" ", inplace=True)
+ df = df.set_axis(
+ [
+ "Rate",
+ "Yld",
+ "Yld Chg",
+ ],
+ axis="columns",
+ )
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-glbonds.png", fig)
+
+ return {"title": "Economy: [WSJ] Global Bonds", "imagefile": imagefile}
diff --git a/bots/economy/grains.py b/bots/economy/grains.py
new file mode 100644
index 00000000000..e715d4579aa
--- /dev/null
+++ b/bots/economy/grains.py
@@ -0,0 +1,70 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def grains_command():
+ """Displays grains futures data [Finviz]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-grains")
+
+ # Retrieve data
+ d_futures = finviz_model.get_futures()
+ df = pd.DataFrame(d_futures["Grains"])
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.fillna("")
+ df = df.set_index("label")
+ df = df.sort_values(by="ticker", ascending=False)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df)
+
+ df = df[
+ [
+ "prevClose",
+ "last",
+ "change",
+ ]
+ ]
+ df.index.names = [""]
+ df = df.rename(
+ columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
+ )
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[6, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-grains.png", fig)
+
+ return {
+ "title": "Economy: [Finviz] Grains Futures",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/indices.py b/bots/economy/indices.py
new file mode 100644
index 00000000000..56e92bc978a
--- /dev/null
+++ b/bots/economy/indices.py
@@ -0,0 +1,67 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def indices_command():
+ """US indices overview [Wall St. Journal]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-indices")
+
+ # Retrieve data
+ df = wsj_model.us_indices()
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Price"] = pd.to_numeric(df["Price"].astype(float))
+ df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
+ df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
+
+ formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
+ for col, value in formats.items():
+ # pylint: disable=W0640
+ df[col] = df[col].map(lambda x: value.format(x))
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df)
+
+ df = df[
+ [
+ "Price",
+ "Chg",
+ "%Chg",
+ ]
+ ]
+
+ df = df.fillna("")
+ df.set_index(" ", inplace=True)
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align=["left", "center"],
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-indices.png", fig)
+ return {
+ "title": "Economy: [WSJ] US Indices",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/meats.py b/bots/economy/meats.py
new file mode 100644
index 00000000000..5afc56f5e40
--- /dev/null
+++ b/bots/economy/meats.py
@@ -0,0 +1,69 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def meats_command():
+ """Displays meats futures data [Finviz]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-meats")
+
+ # Retrieve data
+ d_futures = finviz_model.get_futures()
+ df = pd.DataFrame(d_futures["Meats"])
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df = df.fillna("")
+ df = df.set_index("label")
+ df = df.sort_values(by="ticker", ascending=False)
+
+ formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df)
+
+ df = df[
+ [
+ "prevClose",
+ "last",
+ "change",
+ ]
+ ]
+ df.index.names = [""]
+ df = df.rename(
+ columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
+ )
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[6, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-meats.png", fig)
+ return {
+ "title": "Economy: [Finviz] Meats Futures",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/metals.py b/bots/economy/metals.py
new file mode 100644
index 00000000000..ab3973236eb
--- /dev/null
+++ b/bots/economy/metals.py
@@ -0,0 +1,69 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def metals_command():
+ """Displays metals futures data [Finviz]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-metals")
+
+ # Retrieve data
+ d_futures = finviz_model.get_futures()
+ df = pd.DataFrame(d_futures["Metals"])
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df)
+
+ df = df.sort_values(by="ticker", ascending=False)
+ df = df.fillna("")
+ df.set_index("label", inplace=True)
+
+ df = df[
+ [
+ "prevClose",
+ "last",
+ "change",
+ ]
+ ]
+
+ df.index.names = [""]
+ df = df.rename(
+ columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
+ )
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[6, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-metals.png", fig)
+ return {
+ "title": "Economy: [WSJ] Metals Futures",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/overview.py b/bots/economy/overview.py
new file mode 100644
index 00000000000..61aa8b08d64
--- /dev/null
+++ b/bots/economy/overview.py
@@ -0,0 +1,63 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def overview_command():
+ """Market data overview [Wall St. Journal]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-overview")
+
+ # Retrieve data
+ df = wsj_model.market_overview()
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Price"] = pd.to_numeric(df["Price"].astype(float))
+ df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
+ df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
+
+ formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
+ for col, v in formats.items():
+ df[col] = df[col].map(lambda x: v.format(x)) # pylint: disable=W0640
+
+ df = df[
+ [
+ "Price",
+ "Chg",
+ "%Chg",
+ ]
+ ]
+
+ df = df.fillna("")
+ df.set_index(" ", inplace=True)
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-indices.png", fig)
+ return {
+ "title": "Economy: [WSJ] Market Overview",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/performance.py b/bots/economy/performance.py
new file mode 100644
index 00000000000..f4a6844c387
--- /dev/null
+++ b/bots/economy/performance.py
@@ -0,0 +1,120 @@
+import df2img
+import numpy as np
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def performance_command(economy_group="sector"):
+ """Performance of sectors, industry, country [Finviz]"""
+
+ d_economy_group = {
+ "sector": "Sector",
+ "industry": "Industry",
+ "basic_materials": "Industry (Basic Materials)",
+ "communication_services": "Industry (Communication Services)",
+ "consumer_cyclical": "Industry (Consumer Cyclical)",
+ "consumer_defensive": "Industry (Consumer Defensive)",
+ "energy": "Industry (Energy)",
+ "financial": "Industry (Financial)",
+ "healthcare": "Industry (Healthcare)",
+ "industrials": "Industry (Industrials)",
+ "real_estate": "Industry (Real Estate)",
+ "technology": "Industry (Technology)",
+ "utilities": "Industry (Utilities)",
+ "country": "Country (U.S. listed stocks only)",
+ "capitalization": "Capitalization",
+ }
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-performance %s", economy_group)
+
+ # Select default group
+ if not economy_group:
+ if cfg.DEBUG:
+ logger.debug("Use default economy_group: 'sector'")
+ economy_group = "sector"
+
+ # Check for argument
+ possible_groups = list(d_economy_group.keys())
+
+ if economy_group not in possible_groups:
+ possible_group_list = ", ".join(possible_groups)
+ raise Exception(f"Select a valid group from {possible_group_list}") # nosec
+
+ group = d_economy_group[economy_group]
+
+ # Retrieve data
+ df_group = finviz_model.get_valuation_performance_data(group, "performance")
+
+ # Check for argument
+ if df_group.empty:
+ raise Exception("No available data found")
+
+ # Output data
+ df = pd.DataFrame(df_group)
+ df = df.replace(np.nan, 0)
+
+ df["Volume"] = df["Volume"] / 1_000_000
+ df["Avg Volume"] = df["Avg Volume"] / 1_000_000
+
+ formats = {
+ "Perf Month": "{:.2f}",
+ "Perf Quart": "{:.2f}",
+ "Perf Half": "{:.2f}",
+ "Perf Year": "{:.2f}",
+ "Perf YTD": "{:.2f}",
+ "Avg Volume": "{:.0f}M",
+ "Change": "{:.2f}",
+ "Volume": "{:.0f}M",
+ }
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.set_axis(
+ [
+ "Name",
+ "Week",
+ "Month",
+ "3Month",
+ "6Month",
+ "1Year",
+ "YTD",
+ "Recom",
+ "Avg Vol.",
+ "RelVolume",
+ "Change",
+ "Volume",
+ ],
+ axis="columns",
+ )
+
+ df = df.fillna("")
+ df.set_index("Name", inplace=True)
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(1500, (40 + (50 * dindex))),
+ col_width=[10, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3.5],
+ tbl_cells=dict(
+ align=["left", "center"],
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-performance.png", fig)
+ return {
+ "title": f"Economy: [WSJ] Performance {group}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/softs.py b/bots/economy/softs.py
new file mode 100644
index 00000000000..0ad6d052283
--- /dev/null
+++ b/bots/economy/softs.py
@@ -0,0 +1,71 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def softs_command():
+ """Displays softs futures data [Finviz]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-softs")
+
+ # Retrieve data
+ d_futures = finviz_model.get_futures()
+ df = pd.DataFrame(d_futures["Softs"])
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df)
+
+ df = df.sort_values(by="ticker", ascending=False)
+ df = df.fillna("")
+ df.set_index("label", inplace=True)
+
+ df = df[
+ [
+ "prevClose",
+ "last",
+ "change",
+ ]
+ ]
+
+ df.index.names = [""]
+ df = df.rename(
+ columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
+ )
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[6, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-currencies.png", fig)
+ return {
+ "title": "Economy: [WSJ] Currencies",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/usbonds.py b/bots/economy/usbonds.py
new file mode 100644
index 00000000000..f9c14401013
--- /dev/null
+++ b/bots/economy/usbonds.py
@@ -0,0 +1,69 @@
+import df2img
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import wsj_model
+
+
+def usbonds_command():
+ """US bonds overview [Wall St. Journal]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-usbonds")
+
+ # Retrieve data
+ df = wsj_model.us_bonds()
+
+ # Check for argument
+ if df.empty:
+ raise Exception("No available data found")
+
+ df["Rate (%)"] = pd.to_numeric(df["Rate (%)"].astype(float))
+ df["Yld (%)"] = pd.to_numeric(df["Yld (%)"].astype(float))
+ df["Yld Chg (%)"] = pd.to_numeric(df["Yld Chg (%)"].astype(float))
+
+ formats = {
+ "Rate (%)": "{:.2f}%",
+ "Yld (%)": "{:.2f}%",
+ "Yld Chg (%)": "{:.2f}%",
+ }
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.fillna("")
+ df.set_index(" ", inplace=True)
+
+ df = df.set_axis(
+ [
+ "Rate",
+ "Yld",
+ "Yld Chg",
+ ],
+ axis="columns",
+ )
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[8, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+
+ imagefile = save_image("econ-usbonds.png", fig)
+ return {
+ "title": "Economy: [WSJ] US Bonds",
+ "imagefile": imagefile,
+ }
diff --git a/bots/economy/valuation.py b/bots/economy/valuation.py
new file mode 100644
index 00000000000..e1d38d57452
--- /dev/null
+++ b/bots/economy/valuation.py
@@ -0,0 +1,126 @@
+import df2img
+import numpy as np
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.economy import finviz_model
+
+
+def valuation_command(economy_group="sector"):
+ """Valuation of sectors, industry, country [Finviz]"""
+
+ d_economy_group = {
+ "sector": "Sector",
+ "industry": "Industry",
+ "basic_materials": "Industry (Basic Materials)",
+ "communication_services": "Industry (Communication Services)",
+ "consumer_cyclical": "Industry (Consumer Cyclical)",
+ "consumer_defensive": "Industry (Consumer Defensive)",
+ "energy": "Industry (Energy)",
+ "financial": "Industry (Financial)",
+ "healthcare": "Industry (Healthcare)",
+ "industrials": "Industry (Industrials)",
+ "real_estate": "Industry (Real Estate)",
+ "technology": "Industry (Technology)",
+ "utilities": "Industry (Utilities)",
+ "country": "Country (U.S. listed stocks only)",
+ "capitalization": "Capitalization",
+ }
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("econ-valuation %s", economy_group)
+
+ # Select default group
+ if economy_group == "":
+ if cfg.DEBUG:
+ logger.debug("Use default economy_group: 'sector'")
+ economy_group = "sector"
+
+ # Check for argument
+ possible_groups = list(d_economy_group.keys())
+
+ if economy_group not in possible_groups:
+ raise Exception(
+ f"Select a valid group from {', '.join(possible_groups)}" # nosec
+ )
+
+ group = d_economy_group[economy_group]
+
+ # Retrieve data
+ df_group = finviz_model.get_valuation_performance_data(group, "valuation")
+
+ # Check for argument
+ if df_group.empty:
+ raise Exception("No available data found")
+
+ # Output data
+ df = pd.DataFrame(df_group)
+ df = df.replace(np.nan, 0)
+
+ df = df.set_axis(
+ [
+ "Name",
+ "MarketCap",
+ "P/E",
+ "FwdP/E",
+ "PEG",
+ "P/S",
+ "P/B",
+ "P/C",
+ "P/FCF",
+ "EPSpast5Y",
+ "EPSnext5Y",
+ "Salespast5Y",
+ "Change",
+ "Volume",
+ ],
+ axis="columns",
+ )
+
+ df["P/E"] = pd.to_numeric(df["P/E"].astype(float))
+ df["FwdP/E"] = pd.to_numeric(df["FwdP/E"].astype(float))
+ df["EPSpast5Y"] = pd.to_numeric(df["EPSpast5Y"].astype(float))
+ df["EPSnext5Y"] = pd.to_numeric(df["EPSnext5Y"].astype(float))
+ df["Salespast5Y"] = pd.to_numeric(df["Salespast5Y"].astype(float))
+ df["Volume"] = pd.to_numeric(df["Volume"].astype(float))
+ df["Volume"] = df["Volume"] / 1_000_000
+
+ formats = {
+ "P/E": "{:.2f}",
+ "FwdP/E": "{:.2f}",
+ "EPSpast5Y": "{:.2f}",
+ "EPSnext5Y": "{:.2f}",
+ "Salespast5Y": "{:.2f}",
+ "Change": "{:.2f}",
+ "Volume": "{:.0f}M",
+ }
+ for col, value in formats.items():
+ df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
+
+ df = df.fillna("")
+ df.set_index("Name", inplace=True)
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(1600, (40 + (50 * dindex))),
+ col_width=[12, 5, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 4, 4],
+ tbl_cells=dict(
+ align=["left", "center"],
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("econ-valuation.png", fig)
+ return {
+ "title": "Economy: [Finviz] Valuation",
+ "imagefile": imagefile,
+ }
diff --git a/discordbot/files/bg-dark.png b/bots/files/bg-dark.png
similarity index 100%
rename from discordbot/files/bg-dark.png
rename to bots/files/bg-dark.png
diff --git a/discordbot/files/bg-dark2.png b/bots/files/bg-dark2.png
similarity index 100%
rename from discordbot/files/bg-dark2.png
rename to bots/files/bg-dark2.png
diff --git a/discordbot/files/bg.png b/bots/files/bg.png
similarity index 100%
rename from discordbot/files/bg.png
rename to bots/files/bg.png
diff --git a/discordbot/files/tickers.csv b/bots/files/tickers.csv
similarity index 100%
rename from discordbot/files/tickers.csv
rename to bots/files/tickers.csv
diff --git a/discordbot/generic_commands.py b/bots/generic_commands.py
similarity index 98%
rename from discordbot/generic_commands.py
rename to bots/generic_commands.py
index 0b2ab16b106..50c76c9f13f 100644
--- a/discordbot/generic_commands.py
+++ b/bots/generic_commands.py
@@ -1,5 +1,5 @@
import discord
-import discordbot.config_discordbot as cfg
+import bots.config_discordbot as cfg
class GenericCommands(discord.ext.commands.Cog):
diff --git a/bots/helpers.py b/bots/helpers.py
new file mode 100644
index 00000000000..32cfd9a5ed2
--- /dev/null
+++ b/bots/helpers.py
@@ -0,0 +1,271 @@
+import os
+import uuid
+
+import df2img
+import disnake
+import pandas as pd
+import yfinance as yf
+from numpy.core.fromnumeric import transpose
+from PIL import Image
+
+import bots.config_discordbot as cfg
+
+presets_custom = [
+ "potential_reversals",
+ "golden_cross_penny",
+ "rosenwald_gtfo",
+ "golden_cross",
+ "bull_runs_over_10pct",
+ "recent_growth_and_support",
+ "heavy_inst_ins",
+ "short_squeeze_scan",
+ "under_15dol_stocks",
+ "top_performers_healthcare",
+ "oversold_under_3dol",
+ "value_stocks",
+ "cheap_dividend",
+ "death_cross",
+ "top_performers_tech",
+ "unusual_volume",
+ "cheap_oversold",
+ "undervalue",
+ "high_vol_and_low_debt",
+ "simplistic_momentum_scanner_under_7dol",
+ "5pct_above_low",
+ "growth_stocks",
+ "cheap_bottom_dividend",
+ "analyst_strong_buy",
+ "oversold",
+ "rosenwald",
+ "weak_support_and_top_performers",
+ "channel_up_and_low_debt_and_sma_50and200",
+ "template",
+ "modified_neff",
+ "buffett_like",
+ "oversold_under_5dol",
+ "sexy_year",
+ "news_scanner",
+ "top_performers_all",
+ "stocks_strong_support_levels",
+ "continued_momentum_scan",
+ "modified_dreman",
+ "break_out_stocks",
+]
+signals = [
+ "top_gainers",
+ "top_losers",
+ "new_high",
+ "new_low",
+ "most_volatile",
+ "most_active",
+ "unusual_volume",
+ "overbought",
+ "oversold",
+ "downgrades",
+ "upgrades",
+ "earnings_before",
+ "earnings_after",
+ "recent_insider_buying",
+ "recent_insider_selling",
+ "major_news",
+ "horizontal_sr",
+ "tl_resistance",
+ "tl_support",
+ "wedge_up",
+ "wedge_down",
+ "wedge",
+ "triangle_ascending",
+ "triangle_descending",
+ "channel_up",
+ "channel_down",
+ "channel",
+ "double_top",
+ "double_bottom",
+ "multiple_top",
+ "multiple_bottom",
+ "head_shoulders",
+ "head_shoulders_inverse",
+]
+
+
+def load(ticker, start_date):
+ df_stock_candidate = yf.download(ticker, start=start_date, progress=False)
+ df_stock_candidate.index.name = "date"
+ return df_stock_candidate
+
+
+def quote(ticker):
+ ticker = yf.Ticker(ticker)
+ quote_df = pd.DataFrame(
+ [
+ {
+ "Symbol": ticker.info["symbol"],
+ "Name": ticker.info["shortName"],
+ "Price": ticker.info["regularMarketPrice"],
+ "Open": ticker.info["regularMarketOpen"],
+ "High": ticker.info["dayHigh"],
+ "Low": ticker.info["dayLow"],
+ "Previous Close": ticker.info["previousClose"],
+ "Volume": ticker.info["volume"],
+ "52 Week High": ticker.info["fiftyTwoWeekHigh"],
+ "52 Week Low": ticker.info["fiftyTwoWeekLow"],
+ }
+ ]
+ )
+ quote_df["Change"] = quote_df["Price"] - quote_df["Previous Close"]
+ quote_df["Change %"] = quote_df.apply(
+ lambda x: f'{((x["Change"] / x["Previous Close"]) * 100):.2f}%',
+ axis="columns",
+ )
+ for c in [
+ "Price",
+ "Open",
+ "High",
+ "Low",
+ "Previous Close",
+ "52 Week High",
+ "52 Week Low",
+ "Change",
+ ]:
+ quote_df[c] = quote_df[c].apply(lambda x: f"{x:.2f}")
+ quote_df["Volume"] = quote_df["Volume"].apply(lambda x: f"{x:,}")
+
+ quote_df = quote_df.set_index("Symbol")
+ quote_data = transpose(quote_df)
+ return quote_data
+
+
+def uuid_get():
+ rand = str(uuid.uuid1()).replace("-", "")
+ return rand
+
+
+def autocrop_image(image, border=0):
+ bbox = image.getbbox()
+ image = image.crop(bbox)
+ (width, height) = image.size
+ width += border * 2
+ height += border * 2
+ cropped_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
+ cropped_image.paste(image, (border, border))
+ return cropped_image
+
+
+def ticker_autocomp(inter, ticker: str): # pylint: disable=W0613
+ if not ticker:
+ return ["Start Typing", "for a", "stock ticker"]
+ print(f"ticker_autocomp [ticker]: {ticker}")
+ tlow = ticker.lower()
+ col_list = ["Name"]
+ df = pd.read_csv("files/tickers.csv", usecols=col_list)
+ df = df["Name"]
+ return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
+
+
+def expiry_autocomp(inter, ticker: str): # pylint: disable=W0613
+ data = inter.filled_options["ticker"]
+ yf_ticker = yf.Ticker(data)
+ dates = list(yf_ticker.options)
+ return dates[:24]
+
+
+def presets_custom_autocomp(inter, preset: str): # pylint: disable=W0613
+ df = presets_custom
+ if not preset:
+ return df[:24]
+ plow = preset.lower()
+ print(f"preset_custom_autocomp [preset]: {preset}")
+ return [preset for preset in df if preset.lower().startswith(plow)][:24]
+
+
+def signals_autocomp(inter, signal: str): # pylint: disable=W0613
+ df = signals
+ if not signal:
+ return df[:24]
+ print(f"signal_autocomp [signal]: {signal}")
+ slow = signal.lower()
+ return [signal for signal in df if signal.lower().startswith(slow)][:24]
+
+
+def save_image(file, fig):
+ imagefile = f"{file.replace('.png', '')}_{uuid_get()}.png"
+ df2img.save_dataframe(fig=fig, filename=imagefile)
+ image = Image.open(imagefile)
+ image = autocrop_image(image, 0)
+ image.save(imagefile, "PNG", quality=100)
+ return imagefile
+
+
+def image_border(file, **kwargs):
+ imagefile = f"{file.replace('.png', '')}_{uuid_get()}.png"
+ if "fig" in kwargs:
+ fig = kwargs["fig"]
+ fig.write_image(imagefile)
+ img = Image.open(file)
+ im_bg = Image.open(cfg.IMG_BG)
+ h = img.height + 240
+ w = img.width + 520
+
+ # Paste fig onto background img and autocrop background
+ img = img.resize((w, h), Image.ANTIALIAS)
+ x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
+ y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
+ x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
+ y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
+ img = img.convert("RGB")
+ im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
+ im_bg.save(file, "PNG", quality=100)
+ image = Image.open(file)
+ image = autocrop_image(image, 0)
+ image.save(imagefile, "PNG", quality=100)
+ os.remove(file)
+ return imagefile
+
+
+class ShowView:
+ async def run_discord(self, func, inter, *args, **kwargs):
+ data = func(*args, **kwargs)
+
+ if "view" in data:
+ await inter.send(
+ embed=data["embed"][0],
+ view=data["view"](data["embed"], data["choices"]),
+ )
+
+ else:
+ title = data.get("title", "")
+ embed = disnake.Embed(
+ title=title, colour=cfg.COLOR, description=data.get("description", "")
+ )
+ embed.set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ if "imagefile" in data:
+ image = disnake.File(data["imagefile"])
+ embed.set_image(url=f"attachment://{data['imagefile']}")
+ os.remove(data["imagefile"])
+ await inter.send(embed=embed, file=image)
+ else:
+ await inter.send(embed=embed)
+
+ async def discord(self, func, inter, name, *args, **kwargs):
+ await inter.response.defer()
+ cfg.logger.info(name)
+ if os.environ.get("DEBUG_MODE") == "true":
+ await self.run_discord(func, inter, *args, **kwargs)
+ else:
+ try:
+ await self.run_discord(func, inter, *args, **kwargs)
+ except Exception as e:
+ embed = disnake.Embed(
+ title=name,
+ colour=cfg.COLOR,
+ description=e,
+ )
+ embed.set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+
+ await inter.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/images/gst_logo_lockup_rGreen_with_letters.png b/bots/images/gst_logo_lockup_rGreen_with_letters.png
similarity index 100%
rename from discordbot/images/gst_logo_lockup_rGreen_with_letters.png
rename to bots/images/gst_logo_lockup_rGreen_with_letters.png
diff --git a/discordbot/menus/__init__.py b/bots/menus/__init__.py
similarity index 100%
rename from discordbot/menus/__init__.py
rename to bots/menus/__init__.py
diff --git a/discordbot/menus/menu.py b/bots/menus/menu.py
similarity index 97%
rename from discordbot/menus/menu.py
rename to bots/menus/menu.py
index 2b27146e63a..9c7526b0962 100644
--- a/discordbot/menus/menu.py
+++ b/bots/menus/menu.py
@@ -1,11 +1,11 @@
import time
from typing import List
-import config_discordbot as cfg
-
import disnake
from disnake.ext import commands
+import bots.config_discordbot as cfg
+
bot = commands.Bot()
startTime = time.time()
@@ -40,7 +40,7 @@ class Menu(disnake.ui.View):
def set_link_button(self) -> None:
if not hasattr(self, "link_button"):
- self.link_button = disnake.ui.Button(
+ self.link_button: disnake.ui.Button = disnake.ui.Button(
style=disnake.ButtonStyle.url,
url="https://github.com/GamestonkTerminal/GamestonkTerminal",
label="Site",
diff --git a/discordbot/requirements.txt b/bots/requirements.txt
similarity index 100%
rename from discordbot/requirements.txt
rename to bots/requirements.txt
diff --git a/discordbot/run_discordbot.py b/bots/run_discordbot.py
similarity index 100%
rename from discordbot/run_discordbot.py
rename to bots/run_discordbot.py
diff --git a/discordbot/stocks/__init__.py b/bots/stocks/__init__.py
similarity index 100%
rename from discordbot/stocks/__init__.py
rename to bots/stocks/__init__.py
diff --git a/bots/stocks/candle.py b/bots/stocks/candle.py
new file mode 100644
index 00000000000..4d56023667d
--- /dev/null
+++ b/bots/stocks/candle.py
@@ -0,0 +1,209 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+import yfinance as yf
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def candle_command(
+ ticker: str = "",
+ interval: int = 15,
+ past_days: int = 1,
+ start="",
+ end="",
+):
+ """Shows candle plot of loaded ticker. [Source: Yahoo Finance, IEX Cloud or Alpha Vantage]
+
+ Parameters
+ ----------
+ ticker: str
+ Ticker name
+ interval: int
+ chart mins or daily
+ past_days: int
+ Display the past * days. Default: 1(Not for Daily)
+ start: str
+ start date format YYYY-MM-DD
+ end: str
+ end date format YYYY-MM-DD
+ """
+
+ logger.info("candle %s %s %s %s", ticker, interval, start, end)
+
+ if start == "":
+ start = datetime.now() - timedelta(days=365)
+ else:
+ start = datetime.strptime(start, cfg.DATE_FORMAT)
+ if end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ futures = "=F" or "^"
+ crypto = "-USD"
+ if interval == 1440:
+ df_stock_candidate = yf.download(
+ ticker,
+ start=start,
+ end=end,
+ progress=False,
+ )
+
+ # Check that loading a stock was not successful
+ if df_stock_candidate.empty:
+ raise Exception(f"No data found for {ticker.upper()}")
+
+ df_stock_candidate.index.name = "date"
+ else:
+ s_int = str(interval) + "m"
+ d_granularity = {
+ "1m": past_days,
+ "5m": past_days,
+ "15m": past_days,
+ "30m": past_days,
+ "60m": past_days,
+ }
+ s_start_dt = datetime.utcnow() - timedelta(days=d_granularity[s_int])
+ s_date_start = s_start_dt.strftime("%Y-%m-%d")
+
+ df_stock_candidate = yf.download(
+ ticker,
+ start=s_date_start if s_start_dt > start else start.strftime("%Y-%m-%d"),
+ progress=False,
+ interval=s_int,
+ prepost=True,
+ )
+
+ # Check that loading a stock was not successful
+ if df_stock_candidate.empty:
+ raise Exception(f"No data found for {ticker.upper()}.")
+
+ df_stock_candidate.index = df_stock_candidate.index.tz_localize(None)
+ df_stock_candidate.index.name = "date"
+ df_stock = df_stock_candidate
+ price_df = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_vwap = overlap_model.vwap(price_df, 0)
+
+ plt_title = [f"{ticker.upper()} Intraday {interval}min", "Volume"]
+ title = f"Intraday {interval}min Chart for {ticker.upper()}"
+ if interval == 1440:
+ plt_title = [f"{ticker.upper()} Daily", "Volume"]
+ title = f"Daily Chart for {ticker.upper()}"
+
+ fig = make_subplots(
+ shared_xaxes=True,
+ vertical_spacing=0.09,
+ subplot_titles=plt_title,
+ specs=[[{"secondary_y": True}]],
+ )
+ fig.add_trace(
+ go.Candlestick(
+ x=df_stock.index,
+ open=df_stock.Open,
+ high=df_stock.High,
+ low=df_stock.Low,
+ close=df_stock.Close,
+ name="OHLC",
+ showlegend=False,
+ ),
+ secondary_y=True,
+ )
+ fig.add_trace(
+ go.Bar(
+ x=df_stock.index,
+ y=df_stock.Volume,
+ name="Volume",
+ yaxis="y2",
+ marker_color="#d81aea",
+ opacity=0.4,
+ showlegend=False,
+ ),
+ secondary_y=False,
+ )
+ if interval != 1440:
+ fig.add_trace(
+ go.Scatter(
+ name="VWAP",
+ x=df_stock.index,
+ y=df_vwap["VWAP_D"],
+ opacity=0.65,
+ line=dict(color="#fdc708", width=2),
+ showlegend=True,
+ ),
+ secondary_y=True,
+ )
+ fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across")
+ fig.update_yaxes(showspikes=True, spikethickness=2)
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=40, b=20),
+ template=cfg.PLT_CANDLE_STYLE_TEMPLATE,
+ yaxis2_title="Price ($)",
+ yaxis_title="Volume",
+ yaxis=dict(
+ showgrid=False,
+ fixedrange=False,
+ side="left",
+ titlefont=dict(color="#d81aea"),
+ tickfont=dict(color="#d81aea"),
+ nticks=20,
+ ),
+ yaxis2=dict(
+ side="right",
+ fixedrange=False,
+ anchor="x",
+ layer="above traces",
+ overlaying="y",
+ nticks=20,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ showspikes=True,
+ ),
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ dragmode="pan",
+ hovermode="x unified",
+ spikedistance=1000,
+ hoverdistance=100,
+ )
+ if futures in ticker.upper():
+ fig.update_xaxes(
+ rangebreaks=[
+ dict(bounds=["sat", "sun"]),
+ dict(bounds=[17, 17.50], pattern="hour"),
+ ],
+ )
+ elif crypto not in ticker.upper() and interval != 1440:
+ fig.update_xaxes(
+ rangebreaks=[
+ dict(bounds=["sat", "mon"]),
+ dict(bounds=[20, 9.50], pattern="hour"),
+ ],
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "candle.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/candle_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/candle_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": title,
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/discordbot/stocks/dark_pool_shorts/__init__.py b/bots/stocks/dark_pool_shorts/__init__.py
similarity index 100%
rename from discordbot/stocks/dark_pool_shorts/__init__.py
rename to bots/stocks/dark_pool_shorts/__init__.py
diff --git a/bots/stocks/dark_pool_shorts/dpotc.py b/bots/stocks/dark_pool_shorts/dpotc.py
new file mode 100644
index 00000000000..02aaac73074
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/dpotc.py
@@ -0,0 +1,208 @@
+import plotly.graph_objects as go
+import yfinance as yf
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.dark_pool_shorts import finra_model
+
+
+def dpotc_command(ticker: str = ""):
+ """Dark pools (ATS) vs OTC data [FINRA]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-dpotc %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ ticker = ticker.upper()
+
+ stock = yf.download(ticker, progress=False)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve data
+ ats, otc = finra_model.getTickerFINRAdata(ticker)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(ats.to_string())
+ logger.debug(otc.to_string())
+
+ # Output data
+ title = f"Stocks: [FINRA] Dark Pools (ATS) vs OTC {ticker}"
+
+ if ats.empty and otc.empty:
+ raise Exception("Stock ticker is invalid")
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.4, 0.6],
+ specs=[[{"secondary_y": True}], [{"secondary_y": False}]],
+ )
+
+ if not ats.empty and not otc.empty:
+ fig.add_trace(
+ go.Bar(
+ x=ats.index,
+ y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]),
+ name="ATS",
+ opacity=0.8,
+ ),
+ row=1,
+ col=1,
+ secondary_y=False,
+ )
+ fig.add_trace(
+ go.Bar(
+ x=otc.index,
+ y=otc["totalWeeklyShareQuantity"],
+ name="OTC",
+ opacity=0.8,
+ yaxis="y2",
+ offset=0.0001,
+ ),
+ row=1,
+ col=1,
+ )
+
+ elif not ats.empty:
+ fig.add_trace(
+ go.Bar(
+ x=ats.index,
+ y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]),
+ name="ATS",
+ opacity=0.8,
+ ),
+ row=1,
+ col=1,
+ secondary_y=False,
+ )
+
+ elif not otc.empty:
+ fig.add_trace(
+ go.Bar(
+ x=otc.index,
+ y=otc["totalWeeklyShareQuantity"],
+ name="OTC",
+ opacity=0.8,
+ yaxis="y2",
+ secondary_y=False,
+ ),
+ row=1,
+ col=1,
+ )
+
+ if not ats.empty:
+ fig.add_trace(
+ go.Scatter(
+ name="ATS",
+ x=ats.index,
+ y=ats["totalWeeklyShareQuantity"] / ats["totalWeeklyTradeCount"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ yaxis="y2",
+ ),
+ row=2,
+ col=1,
+ )
+
+ if not otc.empty:
+ fig.add_trace(
+ go.Scatter(
+ name="OTC",
+ x=otc.index,
+ y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
+ line=dict(color="#d81aea", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=2,
+ col=1,
+ )
+ else:
+ fig.add_trace(
+ go.Scatter(
+ name="OTC",
+ x=otc.index,
+ y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
+ line=dict(color="#d81aea", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=2,
+ col=1,
+ )
+
+ fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across")
+ fig.update_yaxes(showspikes=True, spikethickness=2)
+ fig.update_layout(
+ margin=dict(l=20, r=0, t=10, b=20),
+ template=cfg.PLT_CANDLE_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Dark Pools (ATS) vs OTC (Non-ATS) Data for {ticker}",
+ title_x=0.5,
+ yaxis3_title="Shares per Trade",
+ yaxis_title="Total Weekly Shares",
+ xaxis2_title="Weeks",
+ yaxis=dict(
+ fixedrange=False,
+ side="left",
+ nticks=20,
+ ),
+ yaxis2=dict(
+ fixedrange=False,
+ showgrid=False,
+ overlaying="y",
+ anchor="x",
+ layer="above traces",
+ ),
+ yaxis3=dict(
+ fixedrange=False,
+ nticks=10,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ showspikes=True,
+ nticks=20,
+ ),
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ barmode="group",
+ bargap=0.5,
+ bargroupgap=0,
+ dragmode="pan",
+ hovermode="x unified",
+ spikedistance=1000,
+ hoverdistance=100,
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "dps_dpotc.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/dpotc_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/dpotc_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": title,
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/dark_pool_shorts/ftd.py b/bots/stocks/dark_pool_shorts/ftd.py
new file mode 100644
index 00000000000..0cfe52d4a6b
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/ftd.py
@@ -0,0 +1,122 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.dark_pool_shorts import sec_model
+
+
+def ftd_command(ticker: str = "", start="", end=""):
+ """Fails-to-deliver data [SEC]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-ftd %s %s %s", ticker, start, end)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ ticker = ticker.upper()
+
+ if start == "":
+ start = datetime.now() - timedelta(days=365)
+ else:
+ start = datetime.strptime(start, cfg.DATE_FORMAT)
+
+ if end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ # Retrieve data
+ ftds_data = sec_model.get_fails_to_deliver(ticker, start, end, 0)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(ftds_data.to_string())
+
+ stock = helpers.load(ticker, start)
+ stock_ftd = stock[stock.index > start]
+ stock_ftd = stock_ftd[stock_ftd.index < end]
+
+ # Output data
+ fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]])
+
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=stock_ftd.index,
+ y=stock_ftd["Adj Close"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ secondary_y=False,
+ )
+ fig.add_trace(
+ go.Bar(
+ name="FTDs",
+ x=ftds_data["SETTLEMENT DATE"],
+ y=ftds_data["QUANTITY (FAILS)"] / 1000,
+ opacity=1,
+ ),
+ secondary_y=True,
+ )
+ # Set y-axes titles
+ fig.update_yaxes(title_text="Shares [K]", secondary_y=True)
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker}",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ side="right",
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ xaxis2=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ yaxis2=dict(
+ side="left",
+ position=0.15,
+ fixedrange=False,
+ ),
+ hovermode="x unified",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "dps_ftd.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/ftds_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ftds_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: [SEC] Failure-to-deliver {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/dark_pool_shorts/hsi.py b/bots/stocks/dark_pool_shorts/hsi.py
new file mode 100644
index 00000000000..0ec8b736ed3
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/hsi.py
@@ -0,0 +1,80 @@
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.dark_pool_shorts import shortinterest_model
+
+
+def hsi_command(num: int = 10):
+ """Show top high short interest stocks of over 20% ratio [shortinterest.com]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-hsi %s", num)
+
+ # Check for argument
+ if num < 0:
+ raise Exception("Number has to be above 0")
+
+ # Retrieve data
+ df = shortinterest_model.get_high_short_interest()
+ df = df.iloc[1:].head(n=num)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ title = "Stocks: [highshortinterest.com] Top High Short Interest"
+
+ future_column_name = df["Ticker"]
+ df = df.transpose()
+ df.columns = future_column_name
+ df.drop("Ticker")
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = "Overview"
+ i = 1
+ for col_name in df.columns.values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df.columns.values:
+ description = "```" + df[column].fillna("").to_string() + "```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/dark_pool_shorts/pos.py b/bots/stocks/dark_pool_shorts/pos.py
new file mode 100644
index 00000000000..9758394e92c
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/pos.py
@@ -0,0 +1,113 @@
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
+
+
+def pos_command(sort="dpp_dollar", num: int = 10):
+ """Dark pool short position [Stockgrid]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-pos %s %s", sort, num)
+
+ # Check for argument
+ possible_sorts = ("sv", "sv_pct", "nsv", "nsv_dollar", "dpp", "dpp_dollar")
+
+ if sort not in possible_sorts:
+ raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}")
+
+ if num < 0:
+ raise Exception("Number has to be above 0")
+
+ # Retrieve data
+ df = stockgrid_model.get_dark_pool_short_positions(sort, False)
+ df = df.iloc[:num]
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ title = "Stocks: [Stockgrid] Dark Pool Short Position"
+
+ dp_date = df["Date"].values[0]
+ df = df.drop(columns=["Date"])
+ df["Net Short Volume $"] = df["Net Short Volume $"] / 100_000_000
+ df["Short Volume"] = df["Short Volume"] / 1_000_000
+ df["Net Short Volume"] = df["Net Short Volume"] / 1_000_000
+ df["Short Volume %"] = df["Short Volume %"] * 100
+ df["Dark Pools Position $"] = df["Dark Pools Position $"] / (1_000_000_000)
+ df["Dark Pools Position"] = df["Dark Pools Position"] / 1_000_000
+ df.columns = [
+ "Ticker",
+ "Short Vol. (1M)",
+ "Short Vol. %",
+ "Net Short Vol. (1M)",
+ "Net Short Vol. ($100M)",
+ "DP Position (1M)",
+ "DP Position ($1B)",
+ ]
+ future_column_name = df["Ticker"]
+ df = df.transpose()
+ df.columns = future_column_name
+ df.drop("Ticker")
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = "Overview"
+ i = 1
+ for col_name in df.columns.values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df.columns.values:
+ description = (
+ "```The following data corresponds to the date: "
+ + dp_date
+ + "\n\n"
+ + df[column].fillna("").to_string()
+ + "```",
+ )
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/dark_pool_shorts/psi.py b/bots/stocks/dark_pool_shorts/psi.py
new file mode 100644
index 00000000000..69224a17b2a
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/psi.py
@@ -0,0 +1,157 @@
+import plotly.graph_objects as go
+import yfinance as yf
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
+
+
+def psi_command(ticker: str = ""):
+ """Price vs short interest volume [Stockgrid]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-psi %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ ticker = ticker.upper()
+
+ stock = yf.download(ticker, progress=False)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve data
+ df, prices = stockgrid_model.get_short_interest_volume(ticker)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.06,
+ row_width=[0.3, 0.6],
+ specs=[[{"secondary_y": True}], [{"secondary_y": False}]],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df["date"].values,
+ y=prices[len(prices) - len(df) :],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ secondary_y=False,
+ )
+ fig.add_trace(
+ go.Bar(
+ x=df["date"],
+ y=df["total_volume"] / 1_000_000,
+ name="Total Volume",
+ yaxis="y2",
+ ),
+ row=1,
+ col=1,
+ secondary_y=True,
+ )
+ fig.add_trace(
+ go.Bar(
+ x=df["date"],
+ y=df["short_volume"] / 1_000_000,
+ name="Short Volume",
+ yaxis="y2",
+ ),
+ row=1,
+ col=1,
+ secondary_y=True,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="Short Vol. %",
+ x=df["date"].values,
+ y=100 * df["short_volume%"],
+ line=dict(width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=2,
+ col=1,
+ secondary_y=False,
+ )
+ fig.update_traces(hovertemplate="%{y:.2f}")
+ fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across")
+ fig.update_yaxes(showspikes=True, spikethickness=2)
+ fig.update_layout(
+ margin=dict(l=10, r=0, t=40, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Price vs Short Volume Interest for {ticker}",
+ title_x=0.45,
+ yaxis_title="Stock Price ($)",
+ yaxis2_title="FINRA Volume [M]",
+ yaxis3_title="Short Vol. %",
+ yaxis=dict(
+ side="right",
+ fixedrange=False,
+ titlefont=dict(color="#fdc708"),
+ tickfont=dict(color="#fdc708"),
+ nticks=20,
+ ),
+ yaxis2=dict(
+ side="left",
+ fixedrange=False,
+ anchor="x",
+ overlaying="y",
+ titlefont=dict(color="#d81aea"),
+ tickfont=dict(color="#d81aea"),
+ nticks=20,
+ ),
+ yaxis3=dict(
+ fixedrange=False,
+ titlefont=dict(color="#9467bd"),
+ tickfont=dict(color="#9467bd"),
+ nticks=20,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ hovermode="x unified",
+ spikedistance=1000,
+ hoverdistance=100,
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "dps_psi.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/psi_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/psi_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/dark_pool_shorts/shorted.py b/bots/stocks/dark_pool_shorts/shorted.py
new file mode 100644
index 00000000000..c3b1efe8650
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/shorted.py
@@ -0,0 +1,81 @@
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.dark_pool_shorts import yahoofinance_model
+
+
+def shorted_command(num: int = 10):
+ """Show most shorted stocks [Yahoo Finance]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-shorted %s", num)
+
+ # Check for argument
+ if num < 0:
+ raise Exception("Number has to be above 0")
+
+ # Retrieve data
+ df = yahoofinance_model.get_most_shorted().head(num)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ title = "Stocks: [Yahoo Finance] Most Shorted"
+ df.dropna(how="all", axis=1, inplace=True)
+ df = df.replace(float("NaN"), "")
+ future_column_name = df["Symbol"]
+ df = df.transpose()
+ df.columns = future_column_name
+ df.drop("Symbol")
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = "Overview"
+ i = 1
+ for col_name in df.columns.values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df.columns.values:
+ description = "```" + df[column].fillna("").to_string() + "```"
+ embeds.append(
+ disnake.Embed(description=description, colour=cfg.COLOR,).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/dark_pool_shorts/sidtc.py b/bots/stocks/dark_pool_shorts/sidtc.py
new file mode 100644
index 00000000000..95dd4e7048f
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/sidtc.py
@@ -0,0 +1,104 @@
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
+
+
+def sidtc_command(sort="float", num: int = 10):
+ """Short interest and days to cover [Stockgrid]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("dps-sidtc %s %s", sort, num)
+
+ # Check for argument
+ possible_sorts = ("float", "dtc", "si")
+
+ if sort not in possible_sorts:
+ raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}")
+
+ if num < 0:
+ raise Exception("Number has to be above 0")
+
+ # Retrieve data
+ df = stockgrid_model.get_short_interest_days_to_cover(sort)
+ df = df.iloc[:num]
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ title = "Stocks: [Stockgrid] Short Interest and Days to Cover"
+ dp_date = df["Date"].values[0]
+ df = df.drop(columns=["Date"])
+ df["Short Interest"] = df["Short Interest"] / 1_000_000
+ df.head()
+ df.columns = [
+ "Ticker",
+ "Float Short %",
+ "Days to Cover",
+ "Short Interest (1M)",
+ ]
+ future_column_name = df["Ticker"]
+ df = df.transpose()
+ df.columns = future_column_name
+ df.drop("Ticker")
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = "Overview"
+ i = 1
+ for col_name in df.columns.values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title="Dark Pool Shorts", description=initial_str, colour=cfg.COLOR
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+
+ for column in df.columns.values:
+ description = (
+ "```The following data corresponds to the date: "
+ + dp_date
+ + "\n\n"
+ + df[column].fillna("").to_string()
+ + "```",
+ )
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/dark_pool_shorts/spos.py b/bots/stocks/dark_pool_shorts/spos.py
new file mode 100644
index 00000000000..ec1149cb320
--- /dev/null
+++ b/bots/stocks/dark_pool_shorts/spos.py
@@ -0,0 +1,124 @@
+import plotly.graph_objects as go
+import yfinance as yf
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
+
+
+def spos_command(ticker: str = ""):
+ """Net short vs position [Stockgrid]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dps-spos %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ ticker = ticker.upper()
+
+ stock = yf.download(ticker, progress=False)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve data
+ df = stockgrid_model.get_net_short_position(ticker)
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(df.to_string())
+
+ # Output data
+ title = f"Stocks: [Stockgrid] Net Short vs Position {ticker}"
+
+ fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]])
+
+ fig.add_trace(
+ go.Scatter(
+ name="Position ($)",
+ x=df["dates"].values,
+ y=df["dollar_dp_position"] * 1_000,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ secondary_y=True,
+ )
+ fig.add_trace(
+ go.Bar(
+ name="Net Short Vol. ($)",
+ x=df["dates"],
+ y=df["dollar_net_volume"],
+ opacity=1,
+ showlegend=False,
+ ),
+ secondary_y=False,
+ )
+ # Set y-axes titles
+ fig.update_xaxes(dtick="M1", tickformat="%b %d\n%Y")
+ fig.update_yaxes(title_text="Position ($)", secondary_y=True)
+ fig.update_traces(hovertemplate="%{y:.2s}")
+ fig.update_layout(
+ margin=dict(l=0, r=10, t=40, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Net Short Vol. vs Position for {ticker}",
+ title_x=0.5,
+ yaxis_title="Net Short Vol. ($)",
+ yaxis=dict(
+ side="left",
+ showgrid=False,
+ fixedrange=False,
+ layer="above traces",
+ titlefont=dict(color="#d81aea"),
+ tickfont=dict(color="#d81aea"),
+ nticks=10,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ xaxis2=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ yaxis2=dict(
+ side="right",
+ position=0.15,
+ fixedrange=False,
+ titlefont=dict(color="#fdc708"),
+ tickfont=dict(color="#fdc708"),
+ nticks=10,
+ ),
+ hovermode="x unified",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "dps_spos.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/spos_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/spos_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": title,
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/disc/ford.py b/bots/stocks/disc/ford.py
new file mode 100644
index 00000000000..b8723833a78
--- /dev/null
+++ b/bots/stocks/disc/ford.py
@@ -0,0 +1,56 @@
+import df2img
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.stocks.discovery import fidelity_model
+
+
+def ford_command():
+ """Display Orders by Fidelity Customers. [Source: Fidelity]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("disc-ford")
+ order_header, df_orders = fidelity_model.get_orders()
+
+ df_orders = df_orders.head(n=30).iloc[:, :-1]
+ df_orders = df_orders.applymap(str)
+
+ font_color = (
+ ["white"] * 2
+ + [
+ [
+ "#ad0000" if boolv else "#0d5700"
+ for boolv in df_orders["Price Change"].str.contains("-")
+ ]
+ ]
+ + [["white"] * 3]
+ )
+
+ df_orders.set_index("Symbol", inplace=True)
+ df_orders = df_orders.apply(lambda x: x.str.slice(0, 20))
+
+ dindex = len(df_orders.index)
+ fig = df2img.plot_dataframe(
+ df_orders,
+ fig_size=(1500, (40 + (40 * dindex))),
+ col_width=[1, 3, 2.2, 3, 2, 2],
+ tbl_cells=dict(
+ align=["left", "center", "left", "left", "center"],
+ font=dict(color=font_color),
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("disc-ford.png", fig)
+
+ return {
+ "title": "Fidelity Customer Orders",
+ "imagefile": imagefile,
+ }
diff --git a/discordbot/stocks/due_diligence/__init__.py b/bots/stocks/due_diligence/__init__.py
similarity index 100%
rename from discordbot/stocks/due_diligence/__init__.py
rename to bots/stocks/due_diligence/__init__.py
diff --git a/bots/stocks/due_diligence/analyst.py b/bots/stocks/due_diligence/analyst.py
new file mode 100644
index 00000000000..377ee4bae37
--- /dev/null
+++ b/bots/stocks/due_diligence/analyst.py
@@ -0,0 +1,55 @@
+import df2img
+import numpy as np
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.stocks.due_diligence import finviz_model
+
+
+def analyst_command(ticker=""):
+ """Displays analyst recommendations [Finviz]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("dd-analyst %s", ticker)
+
+ # Check for argument
+ if not ticker:
+ raise Exception("Stock ticker is required")
+
+ df = finviz_model.get_analyst_data(ticker)
+ df = df.replace(np.nan, 0)
+ df.index.names = ["Date"]
+ df = df.rename(
+ columns={
+ "category": "Category",
+ "analyst": "Analyst",
+ "rating": "Rating",
+ "target": "Target",
+ "target_from": "Target From",
+ "target_to": "Target To",
+ }
+ )
+
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(1500, (40 + (40 * dindex))),
+ col_width=[5, 5, 8, 14, 5, 5, 5],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("dd-analyst.png", fig)
+
+ return {
+ "title": f"Stocks: [Finviz] Analyst Recommendations {ticker.upper()}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/due_diligence/arktrades.py b/bots/stocks/due_diligence/arktrades.py
new file mode 100644
index 00000000000..adce688cee4
--- /dev/null
+++ b/bots/stocks/due_diligence/arktrades.py
@@ -0,0 +1,117 @@
+import os
+
+import df2img
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import gst_imgur, logger
+from bots.helpers import save_image
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.due_diligence import ark_model
+
+
+def arktrades_command(ticker: str = "", num: int = 10):
+ """Displays trades made by ark [cathiesark.com]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dd-arktrades %s", ticker)
+
+ if ticker:
+ 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["Total"] = ark_holdings["Total"] / 1_000_000
+ ark_holdings.rename(columns={"direction": "B/S", "weight": "F %"}, inplace=True)
+ ark_holdings = ark_holdings.drop(
+ columns=["ticker", "everything.profile.companyName"]
+ )
+
+ ark_holdings.index = pd.Series(ark_holdings.index).apply(
+ lambda x: x.strftime("%Y-%m-%d")
+ )
+
+ df = ark_holdings.head(num)
+ df = df.fillna(0)
+ dindex = len(df.head(num).index)
+ formats = {"Close": "{:.2f}", "Total": "{:.2f}"}
+ for col, f in formats.items():
+ df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640
+
+ title = f"Stocks: [cathiesark.com] {ticker.upper()} Trades by Ark"
+
+ embeds: list = []
+
+ i, i2, end = 0, 0, 20
+ df_pg = []
+ embeds_img = []
+ dindex = len(df.index)
+ while i < dindex:
+ df_pg = df.iloc[i:end]
+ df_pg.append(df_pg)
+ fig = df2img.plot_dataframe(
+ df_pg,
+ fig_size=(900, (40 + (40 * 20))),
+ col_width=[5, 10, 4, 4, 3, 4, 5],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image(f"dd-arktrades{i}.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ image_link = uploaded_image.link
+ embeds_img.append(
+ f"{image_link}",
+ )
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ colour=cfg.COLOR,
+ ),
+ )
+ i2 += 1
+ i += 20
+ end += 20
+ os.remove(imagefile)
+
+ # Author/Footer
+ for i in range(0, i2):
+ embeds[i].set_author(
+ name=cfg.AUTHOR_NAME,
+ url=cfg.AUTHOR_URL,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ embeds[i].set_footer(
+ text=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+
+ i = 0
+ for i in range(0, i2):
+ embeds[i].set_image(url=embeds_img[i])
+
+ i += 1
+ embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
+ choices = [
+ disnake.SelectOption(label="Home", value="0", emoji="🟢"),
+ ]
+
+ return {
+ "view": Menu,
+ "title": title,
+ "embed": embeds,
+ "choices": choices,
+ "embeds_img": embeds_img,
+ }
diff --git a/bots/stocks/due_diligence/customer.py b/bots/stocks/due_diligence/customer.py
new file mode 100644
index 00000000000..a80b3116477
--- /dev/null
+++ b/bots/stocks/due_diligence/customer.py
@@ -0,0 +1,30 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.stocks.due_diligence import csimarket_model
+
+
+def customer_command(ticker: str = ""):
+ """Displays customers of the company [CSIMarket]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dd-customer %s", ticker)
+
+ if not ticker:
+ raise Exception("A ticker is required")
+
+ tickers = csimarket_model.get_customers(ticker)
+
+ if not tickers:
+ raise Exception("Enter a valid ticker")
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(tickers)
+
+ # Output data
+
+ return {
+ "title": f"Stocks: [CSIMarket] {ticker} Customers",
+ "description": tickers,
+ }
diff --git a/bots/stocks/due_diligence/est.py b/bots/stocks/due_diligence/est.py
new file mode 100644
index 00000000000..871ab584b08
--- /dev/null
+++ b/bots/stocks/due_diligence/est.py
@@ -0,0 +1,151 @@
+import os
+
+import df2img
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import gst_imgur, logger
+from bots.helpers import save_image
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.due_diligence import business_insider_model
+
+
+def est_command(ticker: str = ""):
+ """Displays earning estimates [Business Insider]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("dd-est %s", 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:
+ logger.debug(df_year_estimates.to_string())
+ logger.debug(df_quarter_earnings.to_string())
+ logger.debug(df_quarter_revenues.to_string())
+
+ dindex = len(df_year_estimates.index)
+ fig = df2img.plot_dataframe(
+ df_year_estimates,
+ fig_size=(1200, (40 + (60 * dindex))),
+ col_width=[12, 4, 4, 4, 4],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("estimates.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ link_estimates = uploaded_image.link
+
+ os.remove(imagefile)
+
+ fig = df2img.plot_dataframe(
+ df_quarter_earnings,
+ fig_size=(1200, (40 + (40 * 20))),
+ col_width=[5, 5, 4, 4, 5, 4],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("earnings.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ link_earnings = uploaded_image.link
+ os.remove(imagefile)
+
+ fig = df2img.plot_dataframe(
+ df_quarter_revenues,
+ fig_size=(1200, (40 + (40 * 20))),
+ col_width=[5, 5, 4, 4, 5, 4],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("revenues.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ link_revenues = uploaded_image.link
+ os.remove(imagefile)
+
+ embeds = [
+ disnake.Embed(
+ title=f"**{ticker.upper()} Year Estimates**",
+ color=cfg.COLOR,
+ ),
+ disnake.Embed(
+ title=f"**{ticker.upper()} Quarter Earnings**",
+ colour=cfg.COLOR,
+ ),
+ disnake.Embed(
+ title=f"**{ticker.upper()} Quarter Revenues**",
+ colour=cfg.COLOR,
+ ),
+ ]
+ embeds[0].set_image(url=link_estimates)
+ embeds[1].set_image(url=link_earnings)
+ embeds[2].set_image(url=link_revenues)
+ titles = [
+ f"**{ticker.upper()} Year Estimates**",
+ f"**{ticker.upper()} Quarter Earnings**",
+ f"**{ticker.upper()} Quarter Revenues**",
+ ]
+ embeds_img = [
+ f"{link_estimates}",
+ f"{link_earnings}",
+ f"{link_revenues}",
+ ]
+ # Output data
+ choices = [
+ disnake.SelectOption(
+ label=f"{ticker.upper()} Year Estimates", value="0", emoji="🟢"
+ ),
+ disnake.SelectOption(
+ label=f"{ticker.upper()} Quarter Earnings", value="1", emoji="🟢"
+ ),
+ disnake.SelectOption(
+ label=f"{ticker.upper()} Quarter Revenues", value="2", emoji="🟢"
+ ),
+ ]
+
+ return {
+ "view": Menu,
+ "titles": titles,
+ "embed": embeds,
+ "choices": choices,
+ "embeds_img": embeds_img,
+ }
diff --git a/bots/stocks/due_diligence/pt.py b/bots/stocks/due_diligence/pt.py
new file mode 100644
index 00000000000..f6bd1868941
--- /dev/null
+++ b/bots/stocks/due_diligence/pt.py
@@ -0,0 +1,75 @@
+from datetime import datetime, timedelta
+
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+import bots.helpers
+from bots.config_discordbot import logger
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.due_diligence import business_insider_model
+
+
+def pt_command(ticker: str = "", raw: bool = False, start=""):
+ """Displays price targets [Business Insider]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("dd-pt %s", 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 not in [True, False]:
+ raise Exception("raw argument has to be true or false")
+
+ df_analyst_data = business_insider_model.get_price_target_from_analysts(ticker)
+ stock = bots.helpers.load(ticker, start)
+ title = f"Stocks: [Business Insider] Price Targets {ticker}"
+ 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() + "```"
+
+ output = {
+ "title": title,
+ "description": report,
+ }
+ else:
+ plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ 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.upper} (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")
+
+ imagefile = bots.helpers.image_border("ta_pt.png")
+
+ output = {
+ "title": title,
+ "imagefile": imagefile,
+ }
+
+ return output
diff --git a/bots/stocks/due_diligence/sec.py b/bots/stocks/due_diligence/sec.py
new file mode 100644
index 00000000000..c9958f5d1bb
--- /dev/null
+++ b/bots/stocks/due_diligence/sec.py
@@ -0,0 +1,33 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.stocks.due_diligence import marketwatch_model
+
+
+def sec_command(ticker=""):
+ """Displays sec filings [Market Watch]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dd-sec %s", 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:
+ logger.debug(df_financials.to_string())
+
+ df = df_financials
+ df.loc[:, "Link"] = "[Link Source](" + df.loc[:, "Link"].astype(str)
+ df.loc[:, "Link"] = df.loc[:, "Link"] + ")"
+
+ # Output data
+ return {
+ "title": "Stocks: [Market Watch] SEC Filings",
+ "description": df.to_string(),
+ }
diff --git a/bots/stocks/due_diligence/supplier.py b/bots/stocks/due_diligence/supplier.py
new file mode 100644
index 00000000000..125d4c8407a
--- /dev/null
+++ b/bots/stocks/due_diligence/supplier.py
@@ -0,0 +1,29 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.stocks.due_diligence import csimarket_model
+
+
+def supplier_command(ticker=""):
+ """Displays suppliers of the company [CSIMarket]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("dd-supplier %s", ticker)
+
+ if not ticker:
+ raise Exception("A ticker is required")
+
+ tickers = csimarket_model.get_suppliers(ticker)
+
+ if not tickers:
+ raise Exception("Enter a valid ticker")
+
+ # Debug user output
+ if cfg.DEBUG:
+ logger.debug(tickers)
+
+ # Output data
+ return {
+ "title": "Stocks: [CSIMarket] Company Suppliers",
+ "description": tickers,
+ }
diff --git a/discordbot/stocks/government/__init__.py b/bots/stocks/government/__init__.py
similarity index 100%
rename from discordbot/stocks/government/__init__.py
rename to bots/stocks/government/__init__.py
diff --git a/bots/stocks/government/contracts.py b/bots/stocks/government/contracts.py
new file mode 100644
index 00000000000..2aea0894a9f
--- /dev/null
+++ b/bots/stocks/government/contracts.py
@@ -0,0 +1,52 @@
+import pandas as pd
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def contracts_command(
+ ticker: str = "", past_transaction_days: int = 10, raw: bool = False
+):
+ """Displays contracts associated with tickers [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov-contracts %s %s %s", ticker, past_transaction_days, raw)
+
+ if ticker == "":
+ raise Exception("A ticker is required")
+
+ # Retrieve Data
+ df_contracts = quiverquant_model.get_government_trading("contracts", ticker)
+
+ if df_contracts.empty:
+ raise Exception("No government contracts found")
+
+ # Output Data
+ df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]).dt.date
+
+ df_contracts = df_contracts[
+ df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days])
+ ]
+
+ df_contracts.drop_duplicates(inplace=True)
+
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax)
+ ax.set_ylabel("Amount ($1k)")
+ ax.set_title(f"Sum of latest government contracts to {ticker}")
+ fig.tight_layout()
+
+ plt.savefig("gov_contracts.png")
+ imagefile = "gov_contracts.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": f"Stocks: [quiverquant.com] Contracts by {ticker}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/government/gtrades.py b/bots/stocks/government/gtrades.py
new file mode 100644
index 00000000000..7d9bea4da11
--- /dev/null
+++ b/bots/stocks/government/gtrades.py
@@ -0,0 +1,115 @@
+from datetime import datetime, timedelta
+
+import matplotlib.dates as mdates
+import pandas as pd
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def gtrades_command(
+ ticker: str = "",
+ gov_type="",
+ past_transactions_months: int = 10,
+ raw: bool = False,
+):
+ """Displays government trades [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug(
+ "gov-gtrades %s %s %s %s",
+ ticker,
+ gov_type,
+ past_transactions_months,
+ raw,
+ )
+
+ if ticker == "":
+ raise Exception("A ticker is required")
+
+ possible_args = ["congress", "senate", "house"]
+ if gov_type == "":
+ gov_type = "congress"
+ elif gov_type not in possible_args:
+ raise Exception(
+ "Enter a valid government argument, options are: congress, senate and house"
+ )
+
+ # Retrieve Data
+ df_gov = quiverquant_model.get_government_trading(gov_type, ticker)
+
+ if df_gov.empty:
+ raise Exception(f"No {gov_type} trading data found")
+
+ # Output Data
+ df_gov = df_gov.sort_values("TransactionDate", ascending=False)
+
+ start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
+
+ df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
+
+ df_gov = df_gov[df_gov["TransactionDate"] > start_date]
+
+ if df_gov.empty:
+ logger.debug("No recent %s trading data found", gov_type)
+ raise Exception(f"No recent {gov_type} trading data found")
+
+ df_gov["min"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
+ )
+ df_gov["max"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
+ if "-" in x
+ else x.strip("$").replace(",", "").split("\n")[0]
+ )
+
+ df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: int(float(x["min"]))
+ if x["Transaction"] == "Purchase"
+ else -int(float(x["max"])),
+ axis=1,
+ )
+ df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: int(float(x["max"]))
+ if x["Transaction"] == "Purchase"
+ else -1 * int(float(x["min"])),
+ axis=1,
+ )
+
+ df_gov = df_gov.sort_values("TransactionDate", ascending=True)
+
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ ax.fill_between(
+ df_gov["TransactionDate"].unique(),
+ df_gov.groupby("TransactionDate")["lower"].sum().values / 1000,
+ df_gov.groupby("TransactionDate")["upper"].sum().values / 1000,
+ )
+
+ ax.set_xlim(
+ [
+ df_gov["TransactionDate"].values[0],
+ df_gov["TransactionDate"].values[-1],
+ ]
+ )
+ ax.grid()
+ ax.set_title(f"{gov_type.capitalize()} trading on {ticker}")
+ ax.set_xlabel("Date")
+ ax.set_ylabel("Amount ($1k)")
+ plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
+ plt.gcf().autofmt_xdate()
+ fig.tight_layout()
+
+ plt.savefig("gov_gtrades.png")
+ imagefile = "gov_gtrades.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": "Stocks: [quiverquant.com] Government Trades",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/government/histcont.py b/bots/stocks/government/histcont.py
new file mode 100644
index 00000000000..b2f73d8eada
--- /dev/null
+++ b/bots/stocks/government/histcont.py
@@ -0,0 +1,60 @@
+import numpy as np
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def histcont_command(ticker=""):
+ """Displays historical quarterly-contracts [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov-histcont %s", ticker)
+
+ if ticker == "":
+ raise Exception("A ticker is required")
+
+ # Retrieve Data
+ df_contracts = quiverquant_model.get_government_trading(
+ "quarter-contracts", ticker=ticker
+ )
+
+ if df_contracts.empty:
+ logger.debug("No quarterly government contracts found")
+ raise Exception("No quarterly government contracts found")
+
+ # Output Data
+ amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values
+
+ qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values
+ year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values
+
+ quarter_ticks = [
+ f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr)
+ ]
+
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15)
+
+ ax.set_xlim([-0.5, len(amounts) - 0.5])
+ ax.set_xticks(np.arange(0, len(amounts)))
+ ax.set_xticklabels(quarter_ticks)
+ ax.grid()
+ ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}")
+ ax.set_xlabel("Date")
+ ax.set_ylabel("Amount ($1k)")
+ fig.tight_layout()
+
+ plt.savefig("gov_histcont.png")
+ imagefile = "gov_histcont.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": "Stocks: Historical Quarterly Government Contract ",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/government/lastcontracts.py b/bots/stocks/government/lastcontracts.py
new file mode 100644
index 00000000000..13f31a39611
--- /dev/null
+++ b/bots/stocks/government/lastcontracts.py
@@ -0,0 +1,79 @@
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def lastcontracts_command(past_transactions_days: int = 2, num: int = 20):
+ """Displays last government contracts [quiverquant.com]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov lastcontracts %s %s", past_transactions_days, num)
+
+ df_contracts = quiverquant_model.get_government_trading("contracts")
+
+ if df_contracts.empty:
+ logger.debug("No government contracts found")
+ raise Exception("No government contracts found")
+
+ df_contracts.sort_values("Date", ascending=False)
+
+ df_contracts["Date"] = pd.to_datetime(df_contracts["Date"])
+ df_contracts["Date"] = df_contracts["Date"].dt.date
+
+ df_contracts.drop_duplicates(inplace=True)
+ df_contracts = df_contracts[
+ df_contracts["Date"].isin(
+ df_contracts["Date"].unique()[:past_transactions_days]
+ )
+ ]
+
+ df_contracts = df_contracts[["Date", "Ticker", "Amount", "Agency"]][:num]
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ title = "Stocks: [quiverquant.com] Top buy government trading"
+ initial_str = "Overview"
+ i = 1
+ for col_name in df_contracts["Ticker"].values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ embeds = []
+ df_contracts = df_contracts.T
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_contracts.columns.values:
+ description = "```" + df_contracts[column].fillna("").to_string() + "```"
+ embeds.append(
+ disnake.Embed(description=description, colour=cfg.COLOR,).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/government/lasttrades.py b/bots/stocks/government/lasttrades.py
new file mode 100644
index 00000000000..21d34fc8b3f
--- /dev/null
+++ b/bots/stocks/government/lasttrades.py
@@ -0,0 +1,181 @@
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def lasttrades_command(gov_type="", past_days: int = 5, representative=""):
+ """Displays trades made by the congress/senate/house [quiverquant.com]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug(
+ "gov-lasttrades %s %s %s",
+ gov_type,
+ past_days,
+ representative,
+ )
+
+ possible_args = ["congress", "senate", "house"]
+ if gov_type == "":
+ gov_type = "congress"
+ elif gov_type not in possible_args:
+ raise Exception(
+ "Enter a valid government argument, options are: congress, senate and house"
+ )
+
+ # Retrieve Data
+ df_gov = quiverquant_model.get_government_trading(gov_type)
+
+ # Output Data
+ if df_gov.empty:
+ raise Exception(f"No {gov_type} trading data found")
+ df_gov = df_gov.sort_values("TransactionDate", ascending=False)
+
+ df_gov = df_gov[
+ df_gov["TransactionDate"].isin(df_gov["TransactionDate"].unique()[:past_days])
+ ]
+
+ if gov_type == "congress":
+ df_gov = df_gov[
+ [
+ "TransactionDate",
+ "Ticker",
+ "Representative",
+ "Transaction",
+ "Range",
+ "House",
+ "ReportDate",
+ ]
+ ].rename(
+ columns={
+ "TransactionDate": "Transaction Date",
+ "ReportDate": "Report Date",
+ }
+ )
+ else:
+ df_gov = df_gov[
+ [
+ "TransactionDate",
+ "Ticker",
+ "Representative",
+ "Transaction",
+ "Range",
+ ]
+ ].rename(columns={"TransactionDate": "Transaction Date"})
+
+ if representative:
+ df_gov_rep = df_gov[
+ df_gov["Representative"].str.split().str[0] == representative
+ ]
+
+ if df_gov_rep.empty:
+ raise Exception(
+ f"No representative {representative} found in the past {past_days}"
+ f" days. The following are available: "
+ f"{', '.join(df_gov['Representative'].str.split().str[0].unique())}"
+ )
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ title = f"Stocks: [quiverquant.com] Trades by {representative}"
+ initial_str = "Overview"
+ i = 1
+ for col_name in df_gov_rep["Ticker"].values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ embeds = []
+ df_gov_rep = df_gov_rep.T
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports = [f"{initial_str}"]
+ for column in df_gov_rep.columns.values:
+ description = "```" + df_gov_rep[column].fillna("").to_string() + "```"
+ embeds.append(
+ disnake.Embed(description=description, colour=cfg.COLOR,).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ output = {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
+
+ else:
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ title = f"Stocks: [quiverquant.com] Trades for {gov_type.upper()}"
+ initial_str = "Overview"
+ i = 1
+ for col_name in df_gov["Ticker"].values:
+ menu = f"\nPage {i}: {col_name}"
+ initial_str += f"\nPage {i}: {col_name}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ embeds = []
+ df_gov = df_gov.T
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports = [f"{initial_str}"]
+ for column in df_gov.columns.values:
+ description = "```" + df_gov[column].fillna("").to_string() + "```"
+ embeds.append(
+ disnake.Embed(description=description, colour=cfg.COLOR,).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ output = {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
+
+ return output
diff --git a/bots/stocks/government/lobbying.py b/bots/stocks/government/lobbying.py
new file mode 100644
index 00000000000..c9482688036
--- /dev/null
+++ b/bots/stocks/government/lobbying.py
@@ -0,0 +1,92 @@
+from typing import Any, Dict
+import disnake
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def lobbying_command(ticker="", num: int = 10):
+ """Displays lobbying details [quiverquant.com]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov-lobbying %s", ticker)
+
+ if ticker == "":
+ raise Exception("A ticker is required")
+
+ # Retrieve Data
+ df_lobbying = quiverquant_model.get_government_trading(
+ "corporate-lobbying", ticker=ticker
+ )
+
+ if df_lobbying.empty:
+ logger.debug("No corporate lobbying found")
+ raise Exception("No corporate lobbying found")
+
+ # Output Data
+ report = ""
+ title = (f"Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details",)
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ for _, row in (
+ df_lobbying.sort_values(by=["Date"], ascending=False).head(num).iterrows()
+ ):
+ amount = (
+ "$" + str(int(float(row["Amount"]))) if row["Amount"] is not None else "N/A"
+ )
+ report += f"{row['Date']}: {row['Client']} {amount}"
+ if row["Amount"] is not None:
+ report += "\t" + row["Specific_Issue"].replace("\n", " ").replace("\r", "")
+ report += "\n"
+
+ if len(report) <= 4000:
+ description = f"```{report}```"
+ embed = disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ )
+ embed.set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ output: Dict[str, Any] = {
+ "title": title,
+ "description": description,
+ }
+ else:
+ i = 0
+ str_start = 0
+ str_end = 4000
+ embeds = []
+ reports = []
+ while i <= len(report) / 4000:
+ descript = f"```{report[str_start:str_end]}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=descript,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{descript}")
+ str_end = str_start
+ str_start += 4000
+ i += 1
+
+ output = {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
+
+ return output
diff --git a/bots/stocks/government/qtrcontracts.py b/bots/stocks/government/qtrcontracts.py
new file mode 100644
index 00000000000..5d03603e83d
--- /dev/null
+++ b/bots/stocks/government/qtrcontracts.py
@@ -0,0 +1,101 @@
+import numpy as np
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def qtrcontracts_command(num: int = 20, analysis=""):
+ """Displays a look at government contracts [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov-qtrcontracts %s %s", num, analysis)
+
+ possible_args = ["total", "upmom", "downmom"]
+ if analysis == "":
+ analysis = "total"
+ elif analysis not in possible_args:
+ raise Exception(
+ "Enter a valid analysis argument, options are: total, upmom and downmom"
+ )
+
+ # Retrieve Data
+ df_contracts = quiverquant_model.get_government_trading("quarter-contracts")
+
+ if df_contracts.empty:
+ raise Exception("No quarterly government contracts found")
+
+ tickers = quiverquant_model.analyze_qtr_contracts(analysis, num)
+
+ # Output Data
+ if analysis in {"upmom", "downmom"}:
+ description = tickers.to_string()
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ max_amount = 0
+ quarter_ticks = []
+ for symbol in tickers:
+ amounts = (
+ df_contracts[df_contracts["Ticker"] == symbol]
+ .sort_values(by=["Year", "Qtr"])["Amount"]
+ .values
+ )
+
+ qtr = (
+ df_contracts[df_contracts["Ticker"] == symbol]
+ .sort_values(by=["Year", "Qtr"])["Qtr"]
+ .values
+ )
+ year = (
+ df_contracts[df_contracts["Ticker"] == symbol]
+ .sort_values(by=["Year", "Qtr"])["Year"]
+ .values
+ )
+
+ ax.plot(np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15)
+
+ if len(amounts) > max_amount:
+ max_amount = len(amounts)
+ quarter_ticks = [
+ f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr)
+ ]
+
+ ax.set_xlim([-0.5, max_amount - 0.5])
+ ax.set_xticks(np.arange(0, max_amount))
+ ax.set_xticklabels(quarter_ticks)
+ ax.grid()
+ ax.legend(tickers)
+ titles = {
+ "upmom": "Highest increasing quarterly Government Contracts",
+ "downmom": "Highest decreasing quarterly Government Contracts",
+ }
+ ax.set_title(titles[analysis])
+ ax.set_xlabel("Date")
+ ax.set_ylabel("Amount ($1M)")
+ fig.tight_layout()
+
+ plt.savefig("gov_qtrcontracts.png")
+ imagefile = "gov_qtrcontracts.png"
+
+ imagefile = image_border(imagefile)
+ output = {
+ "title": "Stocks: [quiverquant.com] Government Contracts",
+ "imagefile": imagefile,
+ "description": description,
+ }
+
+ elif analysis == "total":
+
+ tickers.index = [ind + " " * (7 - len(ind)) for ind in tickers.index]
+ tickers[:] = [str(round(val[0] / 1e9, 2)) for val in tickers.values]
+ tickers.columns = ["Amount [M]"]
+
+ output = {
+ "title": "Stocks: [quiverquant.com] Government Contracts",
+ "description": tickers.to_string(),
+ }
+
+ return output
diff --git a/bots/stocks/government/topbuys.py b/bots/stocks/government/topbuys.py
new file mode 100644
index 00000000000..71e38c55e8b
--- /dev/null
+++ b/bots/stocks/government/topbuys.py
@@ -0,0 +1,111 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def topbuys_command(
+ gov_type="",
+ past_transactions_months: int = 5,
+ num: int = 20,
+ raw: bool = False,
+):
+ """Displays most purchased stocks by the congress/senate/house [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug(
+ "gov-topbuys %s %s %s %s",
+ gov_type,
+ past_transactions_months,
+ num,
+ raw,
+ )
+
+ possible_args = ["congress", "senate", "house"]
+ if gov_type == "":
+ gov_type = "congress"
+ elif gov_type not in possible_args:
+ raise Exception(
+ "Enter a valid government argument, options are: congress, senate and house"
+ )
+
+ # Retrieve Data
+ df_gov = quiverquant_model.get_government_trading(gov_type)
+
+ if df_gov.empty:
+ logger.debug("No %s trading data found", gov_type)
+ raise Exception("No trading data found")
+
+ df_gov = df_gov.sort_values("TransactionDate", ascending=False)
+ start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
+
+ df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
+
+ df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()
+ # Catch bug where error shown for purchase of >5,000,000
+ df_gov["Range"] = df_gov["Range"].apply(
+ lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x
+ )
+ df_gov["min"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
+ )
+ df_gov["max"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
+ if "-" in x
+ else x.strip("$").replace(",", "")
+ )
+ df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["min"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["max"]),
+ axis=1,
+ )
+ df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["max"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["min"]),
+ axis=1,
+ )
+ description = None
+
+ df_gov = df_gov.sort_values("TransactionDate", ascending=True)
+ if raw:
+ df = pd.DataFrame(
+ df_gov.groupby("Ticker")["upper"]
+ .sum()
+ .div(1000)
+ .sort_values(ascending=False)
+ .head(n=num)
+ )
+ description = "```" + df.to_string() + "```"
+
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values(ascending=False).head(
+ n=num
+ ).plot(kind="bar", rot=0, ax=ax)
+
+ ax.set_ylabel("Amount [1k $]")
+ ax.set_title(
+ f"Top {num} purchased stocks over last {past_transactions_months} "
+ f"months (upper bound) for {gov_type.upper()}"
+ )
+ plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
+ fig.tight_layout()
+
+ plt.savefig("gov_topbuys.png")
+ imagefile = "gov_topbuys.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": f"Stocks: [quiverquant.com] Top purchases for {gov_type.upper()}",
+ "imagefile": imagefile,
+ "description": description,
+ }
diff --git a/bots/stocks/government/toplobbying.py b/bots/stocks/government/toplobbying.py
new file mode 100644
index 00000000000..6e276a3f4eb
--- /dev/null
+++ b/bots/stocks/government/toplobbying.py
@@ -0,0 +1,43 @@
+import pandas as pd
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def toplobbying_command(num: int = 10, raw: bool = False):
+ """Displays top lobbying firms [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("gov-toplobbying %s %s", num, raw)
+
+ # Retrieve Data
+ df_lobbying = quiverquant_model.get_government_trading("corporate-lobbying")
+
+ if df_lobbying.empty:
+ raise Exception("No corporate lobbying found")
+
+ df_lobbying["Amount"] = df_lobbying.Amount.astype(float).fillna(0) / 100_000
+
+ lobbying_by_ticker = pd.DataFrame(
+ df_lobbying.groupby("Ticker")["Amount"].agg("sum")
+ ).sort_values(by="Amount", ascending=False)
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ lobbying_by_ticker.head(num).plot(kind="bar", ax=ax)
+ ax.set_xlabel("Ticker")
+ ax.set_ylabel("Total Amount ($100k)")
+ ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}")
+ fig.tight_layout()
+
+ plt.savefig("ta_toplobbying.png")
+ imagefile = "ta_toplobbying.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": "Stocks: [quiverquant.com] Top Lobbying Firms",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/government/topsells.py b/bots/stocks/government/topsells.py
new file mode 100644
index 00000000000..3a19c18da01
--- /dev/null
+++ b/bots/stocks/government/topsells.py
@@ -0,0 +1,122 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.government import quiverquant_model
+
+
+def topsells_command(
+ gov_type="",
+ past_transactions_months: int = 5,
+ num: int = 10,
+ raw: bool = False,
+):
+ """Displays most sold stocks by the congress/senate/house [quiverquant.com]"""
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug(
+ "gov-topsells %s %s %s %s",
+ gov_type,
+ past_transactions_months,
+ num,
+ raw,
+ )
+
+ possible_args = ["congress", "senate", "house"]
+ if gov_type == "":
+ gov_type = "congress"
+ elif gov_type not in possible_args:
+ raise Exception(
+ "Enter a valid government argument, options are: congress, senate and house"
+ )
+
+ # Retrieve Data
+ df_gov = quiverquant_model.get_government_trading(gov_type)
+
+ # Output Data
+ if df_gov.empty:
+ raise Exception(f"No {gov_type} trading data found\n")
+
+ df_gov = df_gov.sort_values("TransactionDate", ascending=False)
+
+ start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
+
+ df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
+
+ df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()
+
+ df_gov["Range"] = df_gov["Range"].apply(
+ lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x
+ )
+
+ df_gov["min"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[0]
+ .strip("$")
+ .replace(",", "")
+ .strip()
+ .replace(">$", "")
+ .strip()
+ )
+ df_gov["max"] = df_gov["Range"].apply(
+ lambda x: x.split("-")[1]
+ .replace(",", "")
+ .strip()
+ .strip("$")
+ .replace(">$", "")
+ .strip()
+ if "-" in x
+ else x.strip("$").replace(",", "").replace(">$", "").strip()
+ )
+
+ df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["min"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["max"]),
+ axis=1,
+ )
+ df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["max"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["min"]),
+ axis=1,
+ )
+ description = None
+ df_gov = df_gov.sort_values("TransactionDate", ascending=True)
+ if raw:
+ df = pd.DataFrame(
+ df_gov.groupby("Ticker")["upper"]
+ .sum()
+ .div(1000)
+ .sort_values(ascending=True)
+ .abs()
+ .head(n=num)
+ )
+ description = "```" + df.to_string() + "```"
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head(
+ n=num
+ ).plot(kind="bar", rot=0, ax=ax)
+ ax.set_ylabel("Amount ($1k)")
+ ax.set_title(
+ f"{num} most sold stocks over last {past_transactions_months} months"
+ f" (upper bound) for {gov_type}"
+ )
+ plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
+ fig.tight_layout()
+
+ plt.savefig("gov_topsells.png")
+ imagefile = "gov_topsells.png"
+
+ imagefile = image_border(imagefile)
+ return {
+ "title": f"Stocks: [quiverquant.com] Top sells for {gov_type.upper()}",
+ "imagefile": imagefile,
+ "description": description,
+ }
diff --git a/bots/stocks/insider/lins.py b/bots/stocks/insider/lins.py
new file mode 100644
index 00000000000..f4433e338b1
--- /dev/null
+++ b/bots/stocks/insider/lins.py
@@ -0,0 +1,119 @@
+import os
+
+import df2img
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import gst_imgur, logger
+from bots.helpers import save_image
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.insider import finviz_model
+
+
+def lins_command(ticker: str = "", num: int = 10):
+ """Display insider activity for a given stock ticker. [Source: Finviz]
+
+ Parameters
+ ----------
+ ticker : Stock Ticker
+ num : Number of latest insider activity to display
+ """
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("disc-lins %s", num)
+
+ d_finviz_insider = finviz_model.get_last_insider_activity(ticker)
+
+ df = pd.DataFrame.from_dict(d_finviz_insider)
+ df.set_index("Date", inplace=True)
+
+ df = df[
+ [
+ "Relationship",
+ "Transaction",
+ "#Shares",
+ "Cost",
+ "Value ($)",
+ "#Shares Total",
+ "Insider Trading",
+ "SEC Form 4",
+ ]
+ ]
+
+ df = df.head(num)
+ df = df.replace(to_replace="Option Exercise", value="Opt Ex.", regex=True)
+
+ title = f"Insider Trading for {ticker.upper()}"
+
+ embeds: list = []
+
+ i, i2, end = 0, 0, 20
+ df_pg = []
+ embeds_img = []
+ dindex = len(df.index)
+ while i < dindex:
+ df_pg = df.iloc[i:end]
+ df_pg.append(df_pg)
+ fig = df2img.plot_dataframe(
+ df_pg,
+ fig_size=(1600, (40 + (40 * 20))),
+ col_width=[5, 14, 4, 4, 3, 4, 5, 8, 7],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image(f"disc-insider{i}.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ image_link = uploaded_image.link
+ embeds_img.append(
+ f"{image_link}",
+ )
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ colour=cfg.COLOR,
+ ),
+ )
+ i2 += 1
+ i += 20
+ end += 20
+ os.remove(imagefile)
+
+ # Author/Footer
+ for i in range(0, i2):
+ embeds[i].set_author(
+ name=cfg.AUTHOR_NAME,
+ url=cfg.AUTHOR_URL,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ embeds[i].set_footer(
+ text=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+
+ i = 0
+ for i in range(0, i2):
+ embeds[i].set_image(url=embeds_img[i])
+
+ i += 1
+ embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
+ choices = [
+ disnake.SelectOption(label="Home", value="0", emoji="🟢"),
+ ]
+
+ return {
+ "view": Menu,
+ "title": title,
+ "embed": embeds,
+ "choices": choices,
+ "embeds_img": embeds_img,
+ }
diff --git a/discordbot/stocks/options/__init__.py b/bots/stocks/options/__init__.py
similarity index 100%
rename from discordbot/stocks/options/__init__.py
rename to bots/stocks/options/__init__.py
diff --git a/bots/stocks/options/cc_hist.py b/bots/stocks/options/cc_hist.py
new file mode 100644
index 00000000000..c902e404bd2
--- /dev/null
+++ b/bots/stocks/options/cc_hist.py
@@ -0,0 +1,122 @@
+import numpy as np
+import plotly.graph_objects as go
+import yfinance as yf
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.helpers import image_border
+
+
+def cc_hist_command(
+ ticker: str = None, expiry: str = "", strike: float = 10, opt_type: str = ""
+):
+ """Plot historical option prices
+
+ Parameters
+ ----------
+ ticker: str
+ Stock ticker
+ expiry: str
+ expiration date
+ strike: float
+ Option strike price
+ put: bool
+ Calls for call
+ Puts for put
+ """
+
+ # Debug
+ if cfg.DEBUG:
+ print(f"opt-hist {ticker} {strike} {opt_type} {expiry}")
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+ yf_ticker = yf.Ticker(ticker)
+ dates = list(yf_ticker.options)
+
+ if not dates:
+ raise Exception("Stock ticker is invalid")
+
+ options = yf.Ticker(ticker).option_chain(expiry)
+
+ if opt_type == "Calls":
+ options = options.calls
+ if opt_type == "Puts":
+ options = options.puts
+
+ chain_id = options.loc[options.strike == strike, "contractSymbol"].values[0]
+ df_hist = yf.download(chain_id)
+ df_hist.index.name = "date"
+
+ plt_title = [
+ f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical",
+ "Volume",
+ ]
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.06,
+ subplot_titles=plt_title,
+ row_width=[0.2, 0.7],
+ )
+ fig.add_trace(
+ go.Candlestick(
+ x=df_hist.index,
+ open=df_hist.Open,
+ high=df_hist.High,
+ low=df_hist.Low,
+ close=df_hist.Close,
+ name="OHLC",
+ ),
+ row=1,
+ col=1,
+ )
+ colors = [
+ "green" if row.Open < row["Close"] else "red" for _, row in df_hist.iterrows()
+ ] # pylint: disable=E1120
+ fig.add_trace(
+ go.Bar(
+ x=df_hist.index,
+ y=df_hist.Volume,
+ name="Volume",
+ marker_color=colors,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=25, b=5),
+ template=cfg.PLT_CANDLE_STYLE_TEMPLATE,
+ showlegend=False,
+ yaxis_title="Premium",
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ )
+ fig.update_xaxes(
+ rangebreaks=[
+ dict(bounds=["sat", "mon"]),
+ ],
+ )
+ config = dict({"scrollZoom": True})
+ rand = np.random.randint(70000)
+ imagefile = f"opt_hist{rand}.png"
+ fig.write_image(imagefile)
+ imagefile = image_border(imagefile)
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ fig.write_html(f"in/cc_hist_{rand}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cc_hist_{rand}.html)"
+
+ return {
+ "title": f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/hist.py b/bots/stocks/options/hist.py
new file mode 100644
index 00000000000..26372e2e4de
--- /dev/null
+++ b/bots/stocks/options/hist.py
@@ -0,0 +1,154 @@
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots import helpers
+from gamestonk_terminal.stocks.options import syncretism_model
+
+
+def hist_command(
+ ticker: str = None,
+ expiry: str = "",
+ strike: float = 10,
+ opt_type: str = "",
+ greek: str = "",
+):
+ """Plot historical option prices
+
+ Parameters
+ ----------
+ ticker: str
+ Stock ticker
+ expiry: str
+ expiration date
+ strike: float
+ Option strike price
+ put: bool
+ Calls for call
+ Puts for put
+ """
+
+ # Debug
+ if cfg.DEBUG:
+ print(f"opt-hist {ticker} {strike} {opt_type} {expiry} {greek}")
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ if opt_type == "Puts":
+ put = bool(True)
+ if opt_type == "Calls":
+ put = bool(False)
+ chain_id = ""
+
+ df_hist = syncretism_model.get_historical_greeks(
+ ticker, expiry, chain_id, strike, put
+ )
+ title = f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical {greek.upper()}"
+ # Output data
+ fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]])
+
+ fig.add_trace(
+ go.Scatter(
+ name=ticker.upper(),
+ x=df_hist.index,
+ y=df_hist.price,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="Premium",
+ x=df_hist.index,
+ y=df_hist["premium"],
+ opacity=1,
+ yaxis="y2",
+ ),
+ )
+ if greek:
+ fig.add_trace(
+ go.Scatter(
+ name=f"{greek.upper()}",
+ x=df_hist.index,
+ y=df_hist[greek],
+ opacity=1,
+ yaxis="y3",
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=10, r=10, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=title,
+ title_x=0.03,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ side="right",
+ fixedrange=False,
+ titlefont=dict(color="#fdc708"),
+ tickfont=dict(color="#fdc708"),
+ position=0.02,
+ nticks=20,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ domain=[0.037, 1],
+ ),
+ xaxis2=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ xaxis3=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ fixedrange=False,
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ yaxis2=dict(
+ side="left",
+ fixedrange=False,
+ anchor="x",
+ overlaying="y",
+ titlefont=dict(color="#d81aea"),
+ tickfont=dict(color="#d81aea"),
+ nticks=20,
+ ),
+ yaxis3=dict(
+ side="left",
+ position=0,
+ fixedrange=False,
+ overlaying="y",
+ titlefont=dict(color="#00e6c3"),
+ tickfont=dict(color="#00e6c3"),
+ nticks=20,
+ ),
+ hovermode="x unified",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "opt_hist.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/hist_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hist_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/iv.py b/bots/stocks/options/iv.py
new file mode 100644
index 00000000000..6a1d225f5f7
--- /dev/null
+++ b/bots/stocks/options/iv.py
@@ -0,0 +1,50 @@
+import df2img
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.stocks.options import barchart_model
+
+
+def iv_command(ticker: str = None):
+ """Options IV"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-iv %s", ticker)
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ df = barchart_model.get_options_info(ticker)
+ df = df.fillna("")
+ df = df.set_axis(
+ [
+ " ",
+ "",
+ ],
+ axis="columns",
+ )
+ df.set_index(" ", inplace=True)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(600, 1500),
+ col_width=[3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("opt-iv.png", fig)
+
+ return {
+ "title": f"{ticker.upper()} Options: IV",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/oi.py b/bots/stocks/options/oi.py
new file mode 100644
index 00000000000..9319d6c4446
--- /dev/null
+++ b/bots/stocks/options/oi.py
@@ -0,0 +1,137 @@
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.options import op_helpers, yfinance_model
+
+
+def oi_command(
+ ticker: str = None,
+ expiry: str = "",
+ min_sp: float = None,
+ max_sp: float = None,
+):
+ """Options OI"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-oi %s %s %s %s", ticker, expiry, min_sp, max_sp)
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ dates = yfinance_model.option_expirations(ticker)
+
+ if not dates:
+ raise Exception("Stock ticker is invalid")
+
+ options = yfinance_model.get_option_chain(ticker, expiry)
+ calls = options.calls
+ puts = options.puts
+ current_price = yfinance_model.get_price(ticker)
+
+ min_strike = 0.75 * current_price
+ max_strike = 1.95 * current_price
+
+ if len(calls) > 40:
+ min_strike = 0.75 * current_price
+ max_strike = 1.25 * current_price
+
+ if min_sp:
+ min_strike = min_sp
+ if max_sp:
+ max_strike = max_sp
+
+ call_oi = calls.set_index("strike")["openInterest"] / 1000
+ put_oi = puts.set_index("strike")["openInterest"] / 1000
+
+ df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
+ df_opt = df_opt.rename(
+ columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
+ )
+
+ max_pain = op_helpers.calculate_max_pain(df_opt)
+ fig = go.Figure()
+
+ dmax = df_opt[["OI_call", "OI_put"]].values.max()
+ dmin = df_opt[["OI_call", "OI_put"]].values.min()
+ fig.add_trace(
+ go.Scatter(
+ x=df_opt.index,
+ y=df_opt["OI_call"],
+ name="Calls",
+ mode="lines+markers",
+ line=dict(color="green", width=3),
+ )
+ )
+
+ fig.add_trace(
+ go.Scatter(
+ x=df_opt.index,
+ y=df_opt["OI_put"],
+ name="Puts",
+ mode="lines+markers",
+ line=dict(color="red", width=3),
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=[current_price, current_price],
+ y=[dmin, dmax],
+ mode="lines",
+ line=dict(color="gold", width=2),
+ name="Current Price",
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=[max_pain, max_pain],
+ y=[dmin, dmax],
+ mode="lines",
+ line=dict(color="grey", width=3, dash="dash"),
+ name=f"Max Pain: {max_pain}",
+ )
+ )
+ fig.update_xaxes(
+ range=[min_strike, max_strike],
+ constrain="domain",
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=60, b=20),
+ template=cfg.PLT_SCAT_STYLE_TEMPLATE,
+ title=f"Open Interest for {ticker.upper()} expiring {expiry}",
+ title_x=0.5,
+ legend_title="",
+ xaxis_title="Strike",
+ yaxis_title="Open Interest (1k)",
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ ),
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ dragmode="pan",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "opt_oi.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/oi_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Open Interest for {ticker.upper()} expiring {expiry}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/opt_chain.py b/bots/stocks/options/opt_chain.py
new file mode 100644
index 00000000000..8f08d332561
--- /dev/null
+++ b/bots/stocks/options/opt_chain.py
@@ -0,0 +1,151 @@
+import os
+
+import df2img
+import disnake
+import numpy as np
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import gst_imgur, logger
+from bots.helpers import save_image
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.options import yfinance_model
+
+
+def chain_command(
+ ticker: str = None,
+ expiry: str = None,
+ opt_type: str = None,
+ min_sp: float = None,
+ max_sp: float = None,
+):
+ """Show calls/puts for given ticker and expiration"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "opt-chain %s %s %s %s %s", ticker, expiry, opt_type, min_sp, max_sp
+ )
+
+ # Check for argument
+ if not ticker:
+ raise Exception("Stock ticker is required")
+
+ dates = yfinance_model.option_expirations(ticker)
+
+ if not dates:
+ raise Exception("Stock ticker is invalid")
+
+ options = yfinance_model.get_option_chain(ticker, str(expiry))
+ calls_df = options.calls.fillna(0)
+ puts_df = options.puts.fillna(0)
+
+ column_map = {"openInterest": "oi", "volume": "vol", "impliedVolatility": "iv"}
+ columns = [
+ "strike",
+ "bid",
+ "ask",
+ "volume",
+ "openInterest",
+ "impliedVolatility",
+ ]
+
+ if opt_type == "Calls":
+ df = calls_df[columns].rename(columns=column_map)
+ if opt_type == "Puts":
+ df = puts_df[columns].rename(columns=column_map)
+
+ min_strike = np.percentile(df["strike"], 1)
+ max_strike = np.percentile(df["strike"], 100)
+
+ if min_sp:
+ min_strike = min_sp
+ if max_sp:
+ max_strike = max_sp
+ if min_sp > max_sp: # type: ignore
+ min_sp, max_sp = max_strike, min_strike
+
+ df = df[df["strike"] >= min_strike]
+ df = df[df["strike"] <= max_strike]
+
+ df["iv"] = pd.to_numeric(df["iv"].astype(float))
+
+ formats = {"iv": "{:.2f}"}
+ for col, f in formats.items():
+ df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640
+ df.set_index("strike", inplace=True)
+
+ title = (
+ f"Stocks: {opt_type} Option Chain for {ticker.upper()} on {expiry} [yfinance]"
+ )
+
+ embeds: list = []
+ # Output
+ i, i2, end = 0, 0, 20
+ df_pg = []
+ embeds_img = []
+ dindex = len(df.index)
+ while i < dindex:
+ df_pg = df.iloc[i:end]
+ df_pg.append(df_pg)
+ fig = df2img.plot_dataframe(
+ df_pg,
+ fig_size=(1000, (40 + (40 * 20))),
+ col_width=[3, 3, 3, 3],
+ tbl_cells=dict(
+ height=35,
+ ),
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ template="plotly_dark",
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image(f"opt-chain{i}.png", fig)
+
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ image_link = uploaded_image.link
+ embeds_img.append(
+ f"{image_link}",
+ )
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ colour=cfg.COLOR,
+ ),
+ )
+ i2 += 1
+ i += 20
+ end += 20
+ os.remove(imagefile)
+
+ # Author/Footer
+ for i in range(0, i2):
+ embeds[i].set_author(
+ name=cfg.AUTHOR_NAME,
+ url=cfg.AUTHOR_URL,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ embeds[i].set_footer(
+ text=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+
+ i = 0
+ for i in range(0, i2):
+ embeds[i].set_image(url=embeds_img[i])
+
+ i += 1
+ embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
+ choices = [
+ disnake.SelectOption(label="Home", value="0", emoji="🟢"),
+ ]
+
+ return {
+ "view": Menu,
+ "title": title,
+ "embed": embeds,
+ "choices": choices,
+ "embeds_img": embeds_img,
+ }
diff --git a/bots/stocks/options/overview.py b/bots/stocks/options/overview.py
new file mode 100644
index 00000000000..330ae65fb6e
--- /dev/null
+++ b/bots/stocks/options/overview.py
@@ -0,0 +1,380 @@
+import os
+
+import df2img
+import disnake
+import numpy as np
+import pandas as pd
+import plotly.graph_objects as go
+import yfinance as yf
+
+import bots.config_discordbot as cfg
+from bots import helpers
+from bots.config_discordbot import gst_imgur, logger
+from bots.menus.menu import Menu
+from gamestonk_terminal.stocks.options import op_helpers, yfinance_model
+from gamestonk_terminal.stocks.options.barchart_model import get_options_info
+
+
+# pylint: disable=R0914
+# pylint: disable=R0915
+def overview_command(
+ ticker: str = None,
+ expiry: str = None,
+ min_sp: float = None,
+ max_sp: float = None,
+):
+ """Options Overview"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-overview %s %s %s %s", ticker, expiry, min_sp, max_sp)
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ # Get options info/dates, Look for logo_url
+ df = get_options_info(ticker) # Barchart Options IV Overview
+
+ dates = yfinance_model.option_expirations(ticker) # Expiration dates
+ tup = f"{ticker.upper()}"
+ url = yf.Ticker(tup).info["logo_url"]
+ url += "?raw=true" if url else ""
+
+ if not dates:
+ raise Exception("Stock ticker is invalid")
+
+ options = yfinance_model.get_option_chain(ticker, str(expiry))
+ calls = options.calls.fillna(0)
+ puts = options.puts.fillna(0)
+ current_price = yfinance_model.get_price(ticker)
+
+ min_strike2 = np.percentile(calls["strike"], 1)
+ max_strike2 = np.percentile(calls["strike"], 100)
+ min_strike = 0.75 * current_price
+ max_strike = 1.95 * current_price
+
+ if len(calls) > 40:
+ min_strike = 0.75 * current_price
+ max_strike = 1.25 * current_price
+
+ if min_sp:
+ min_strike = min_sp
+ min_strike2 = min_sp
+ if max_sp:
+ max_strike = max_sp
+ max_strike2 = max_sp
+ if min_sp > max_sp: # type: ignore
+ min_sp, max_sp = max_strike2, min_strike2
+
+ call_oi = calls.set_index("strike")["openInterest"] / 1000
+ put_oi = puts.set_index("strike")["openInterest"] / 1000
+
+ df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
+ df_opt = df_opt.rename(
+ columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
+ )
+
+ max_pain = op_helpers.calculate_max_pain(df_opt)
+ fig = go.Figure()
+
+ dmax = df_opt[["OI_call", "OI_put"]].values.max()
+ dmin = df_opt[["OI_call", "OI_put"]].values.min()
+ fig.add_trace(
+ go.Scatter(
+ x=df_opt.index,
+ y=df_opt["OI_call"],
+ name="Calls",
+ mode="lines+markers",
+ line=dict(color="green", width=3),
+ )
+ )
+
+ fig.add_trace(
+ go.Scatter(
+ x=df_opt.index,
+ y=df_opt["OI_put"],
+ name="Puts",
+ mode="lines+markers",
+ line=dict(color="red", width=3),
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=[current_price, current_price],
+ y=[dmin, dmax],
+ mode="lines",
+ line=dict(color="gold", width=2),
+ name="Current Price",
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=[max_pain, max_pain],
+ y=[dmin, dmax],
+ mode="lines",
+ line=dict(color="grey", width=3, dash="dash"),
+ name=f"Max Pain: {max_pain}",
+ )
+ )
+ fig.update_xaxes(
+ range=[min_strike, max_strike],
+ constrain="domain",
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=60, b=20),
+ template=cfg.PLT_SCAT_STYLE_TEMPLATE,
+ title=f"Open Interest for {ticker.upper()} expiring {expiry}",
+ title_x=0.5,
+ legend_title="",
+ xaxis_title="Strike",
+ yaxis_title="Open Interest (1k)",
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ ),
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ dragmode="pan",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "opt_oi.png"
+
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/oi_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ uploaded_image_oi = gst_imgur.upload_image(imagefile, title="something")
+ image_link_oi = uploaded_image_oi.link
+ os.remove(imagefile)
+
+ column_map = {"openInterest": "oi", "volume": "vol", "impliedVolatility": "iv"}
+ columns = [
+ "strike",
+ "bid",
+ "ask",
+ "volume",
+ "openInterest",
+ "impliedVolatility",
+ ]
+
+ calls_df = calls[columns].rename(columns=column_map)
+ puts_df = puts[columns].rename(columns=column_map)
+
+ calls_df = calls_df[calls_df["strike"] >= min_strike2]
+ calls_df = calls_df[calls_df["strike"] <= max_strike2]
+ puts_df = puts_df[puts_df["strike"] >= min_strike2]
+ puts_df = puts_df[puts_df["strike"] <= max_strike2]
+
+ calls_df["iv"] = pd.to_numeric(calls_df["iv"].astype(float))
+ puts_df["iv"] = pd.to_numeric(puts_df["iv"].astype(float))
+
+ formats = {"iv": "{:.2f}"}
+ for col, f in formats.items():
+ calls_df[col] = calls_df[col].map(
+ lambda x: f.format(x) # pylint: disable=W0640
+ )
+ puts_df[col] = puts_df[col].map(lambda x: f.format(x)) # pylint: disable=W0640
+
+ calls_df.set_index("strike", inplace=True)
+ puts_df.set_index("strike", inplace=True)
+
+ if "-" in df.iloc[0, 1]:
+ iv = f"```diff\n- {df.iloc[0, 1]}\n```"
+ else:
+ iv = f"```yaml\n {df.iloc[0, 1]}\n```"
+
+ pfix, sfix = f"{ticker.upper()} ", f" expiring {expiry}"
+ if expiry == dates[0]:
+ pfix = f"{ticker.upper()} Weekly "
+ sfix = ""
+
+ titles = [f"{ticker.upper()} Overview", f"{pfix}Open Interest{sfix}"]
+ embeds = [
+ disnake.Embed(
+ title=f"{ticker.upper()} Overview",
+ color=cfg.COLOR,
+ ),
+ disnake.Embed(
+ title=f"{pfix}Open Interest{sfix}",
+ description=plt_link,
+ colour=cfg.COLOR,
+ ),
+ ]
+ choices = [
+ disnake.SelectOption(label=f"{ticker.upper()} Overview", value="0", emoji="🟢"),
+ disnake.SelectOption(label=f"{pfix}Open Interest{sfix}", value="1", emoji="🟢"),
+ ]
+ embeds_img = []
+
+ i, i2, end = 0, 0, 20
+ df_calls = []
+ dindex = len(calls_df.index)
+ while i <= dindex:
+ df_calls = calls_df.iloc[i:end]
+ df_calls.append(df_calls)
+ figc = df2img.plot_dataframe(
+ df_calls,
+ fig_size=(1000, (40 + (40 * 20))),
+ col_width=[3, 3, 3, 3],
+ tbl_cells=cfg.PLT_TBL_CELLS,
+ font=cfg.PLT_TBL_FONT,
+ template=cfg.PLT_TBL_STYLE_TEMPLATE,
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = helpers.save_image("opt-calls.png", figc)
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ image_link = uploaded_image.link
+ embeds_img.append(
+ f"{image_link}",
+ )
+ titles.append(f"{pfix}Calls{sfix}")
+ embeds.append(
+ disnake.Embed(
+ title=f"{pfix}Calls{sfix}",
+ colour=cfg.COLOR,
+ ),
+ )
+ i2 += 1
+ i += 20
+ end += 20
+ os.remove(imagefile)
+
+ # Add Calls page field
+ i, page, puts_page = 2, 0, 3
+ i3 = i2 + 2
+ choices.append(
+ disnake.SelectOption(label="Calls Page 1", value="2", emoji="🟢"),
+ )
+ for i in range(2, i3):
+ page += 1
+ puts_page += 1
+
+ embeds[i].add_field(name=f"Calls Page {page}", value="_ _", inline=True)
+
+ # Puts Pages
+ i, end = 0, 20
+ df_puts = []
+
+ dindex = len(puts_df.index)
+ while i <= dindex:
+ df_puts = puts_df.iloc[i:end]
+ df_puts.append(df_puts)
+ figp = df2img.plot_dataframe(
+ df_puts,
+ fig_size=(1000, (40 + (40 * 20))),
+ col_width=[3, 3, 3, 3],
+ tbl_cells=cfg.PLT_TBL_CELLS,
+ font=cfg.PLT_TBL_FONT,
+ template=cfg.PLT_TBL_STYLE_TEMPLATE,
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = helpers.save_image("opt-puts.png", figp)
+ uploaded_image = gst_imgur.upload_image(imagefile, title="something")
+ image_link = uploaded_image.link
+ embeds_img.append(
+ f"{image_link}",
+ )
+ titles.append(f"{pfix}Puts{sfix}")
+ embeds.append(
+ disnake.Embed(
+ title=f"{pfix}Puts{sfix}",
+ colour=cfg.COLOR,
+ ),
+ )
+ i2 += 1
+ i += 20
+ end += 20
+ os.remove(imagefile)
+
+ # Add Puts page field
+ i, page = 0, 0
+ puts_page -= 1
+ i2 += 2
+ choices.append(
+ disnake.SelectOption(label="Puts Page 1", value=f"{puts_page}", emoji="🟢"),
+ )
+ for i in range(puts_page, i2):
+ page += 1
+ embeds[i].add_field(name=f"Puts Page {page}", value="_ _", inline=True)
+
+ # Author/Footer
+ for i in range(0, i2):
+ embeds[i].set_author(
+ name=cfg.AUTHOR_NAME,
+ url=cfg.AUTHOR_URL,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ embeds[i].set_footer(
+ text=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+
+ # Set images to Pages
+ i = 0
+ img_i = 0
+ embeds[1].set_image(url=image_link_oi)
+ for i in range(2, i2):
+ embeds[i].set_image(url=embeds_img[img_i])
+ img_i += 1
+ i += 1
+
+ if url:
+ embeds[0].set_thumbnail(url=f"{url}")
+ else:
+ embeds[0].set_thumbnail(url=cfg.AUTHOR_ICON_URL)
+
+ # Overview Section
+ embeds[0].add_field(name=f"{df.iloc[0, 0]}", value=iv, inline=False)
+
+ embeds[0].add_field(
+ name=f"•{df.iloc[1, 0]}", value=f"```css\n{df.iloc[1, 1]}\n```", inline=True
+ )
+ for N in range(2, 6):
+ embeds[0].add_field(
+ name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
+ value=f"```css\n{df.iloc[N, 1]}\n```",
+ inline=True,
+ )
+
+ embeds[0].add_field(name="_ _", value="_ _", inline=False)
+ for N in range(6, 8):
+ embeds[0].add_field(
+ name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
+ value=f"```css\n{df.iloc[N, 1]}\n```",
+ inline=True,
+ )
+
+ embeds[0].add_field(name="_ _", value="_ _", inline=False)
+ for N in range(8, 10):
+ embeds[0].add_field(
+ name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
+ value=f"```css\n{df.iloc[N, 1]}\n```",
+ inline=True,
+ )
+
+ embeds[0].add_field(name="_ _", value="_ _", inline=False)
+ for N in range(10, 12):
+ embeds[0].add_field(
+ name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
+ value=f"```css\n{df.iloc[N, 1]}\n```",
+ inline=True,
+ )
+
+ embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
+ os.remove(imagefile)
+
+ return {
+ "view": Menu,
+ "titles": titles,
+ "embed": embeds,
+ "choices": choices,
+ "embeds_img": embeds_img,
+ }
diff --git a/bots/stocks/options/unu.py b/bots/stocks/options/unu.py
new file mode 100644
index 00000000000..ff447fc1414
--- /dev/null
+++ b/bots/stocks/options/unu.py
@@ -0,0 +1,96 @@
+import df2img
+import numpy as np
+import pandas as pd
+import requests
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import save_image
+from gamestonk_terminal.helper_funcs import get_user_agent
+
+
+def unu_command(num: int = None):
+ """Unusual Options"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-unu %s", num)
+
+ # Check for argument
+ if num is None:
+ num = 10
+
+ pages = np.arange(0, num // 20 + 1)
+ data_list = []
+ for page_num in pages:
+
+ r = requests.get(
+ f"https://app.fdscanner.com/api2/unusualvolume?p=0&page_size=20&page={int(page_num)}",
+ headers={"User-Agent": get_user_agent()},
+ )
+
+ if r.status_code != 200:
+ logger.debug("Error in fdscanner request")
+ return pd.DataFrame(), "request error"
+
+ data_list.append(r.json())
+
+ ticker, expiry, option_strike, option_type, ask, bid, oi, vol, voi = (
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ )
+ for data in data_list:
+ for entry in data["data"]:
+ ticker.append(entry["tk"])
+ expiry.append(entry["expiry"])
+ option_strike.append(float(entry["s"]))
+ option_type.append("Put" if entry["t"] == "P" else "Call")
+ ask.append(entry["a"])
+ bid.append(entry["b"])
+ oi.append(entry["oi"])
+ vol.append(entry["v"])
+ voi.append(entry["vol/oi"])
+
+ df = pd.DataFrame(
+ {
+ "Ticker": ticker,
+ "Exp": expiry,
+ "Strike": option_strike,
+ "Type": option_type,
+ "Vol/OI": voi,
+ "Vol": vol,
+ "OI": oi,
+ }
+ )
+
+ df = df.replace({"2021-", "2022-"}, "", regex=True)
+ df.set_index("Ticker", inplace=True)
+ dindex = len(df.index)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(800, (40 + (40 * dindex))),
+ col_width=[3, 3, 3, 3, 3, 3, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("opt-unu.png", fig)
+
+ return {
+ "title": "Unusual Options",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/vol.py b/bots/stocks/options/vol.py
new file mode 100644
index 00000000000..5e59d20cbf2
--- /dev/null
+++ b/bots/stocks/options/vol.py
@@ -0,0 +1,119 @@
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.options import yfinance_model
+
+
+def vol_command(
+ ticker: str = None,
+ expiry: str = "",
+ min_sp: float = None,
+ max_sp: float = None,
+):
+ """Options VOL"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-vol %s %s %s %s", ticker, expiry, min_sp, max_sp)
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ dates = yfinance_model.option_expirations(ticker)
+
+ if not dates:
+ raise Exception("Stock ticker is invalid")
+
+ options = yfinance_model.get_option_chain(ticker, expiry)
+ current_price = yfinance_model.get_price(ticker)
+
+ if min_sp is None:
+ min_strike = 0.75 * current_price
+ else:
+ min_strike = min_sp
+
+ if max_sp is None:
+ max_strike = 1.25 * current_price
+ else:
+ max_strike = max_sp
+
+ calls = options.calls
+ puts = options.puts
+ call_v = calls.set_index("strike")["volume"] / 1000
+ put_v = puts.set_index("strike")["volume"] / 1000
+
+ df_opt = pd.merge(put_v, call_v, left_index=True, right_index=True)
+ dmax = df_opt.values.max()
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ x=call_v.index,
+ y=call_v.values,
+ name="Calls",
+ mode="lines+markers",
+ line=dict(color="green", width=3),
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=put_v.index,
+ y=put_v.values,
+ name="Puts",
+ mode="lines+markers",
+ line=dict(color="red", width=3),
+ )
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=[current_price, current_price],
+ y=[0, dmax],
+ mode="lines",
+ line=dict(color="gold", width=2),
+ name="Current Price",
+ )
+ )
+ fig.update_xaxes(
+ range=[min_strike, max_strike],
+ constrain="domain",
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=60, b=20),
+ template=cfg.PLT_SCAT_STYLE_TEMPLATE,
+ title=f"Volume for {ticker.upper()} expiring {expiry}",
+ title_x=0.5,
+ legend_title="",
+ xaxis_title="Strike",
+ yaxis_title="Volume (1k)",
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ ),
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ dragmode="pan",
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "opt_vol.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/vol_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Volume for {ticker.upper()} expiring {expiry}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/options/vsurf.py b/bots/stocks/options/vsurf.py
new file mode 100644
index 00000000000..950984d451b
--- /dev/null
+++ b/bots/stocks/options/vsurf.py
@@ -0,0 +1,124 @@
+import numpy as np
+import plotly.graph_objects as go
+from scipy.spatial import Delaunay
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.stocks.options import yfinance_model
+
+
+def vsurf_command(
+ ticker: str = "",
+ z: str = "IV",
+):
+ """Display vol surface
+
+ Parameters
+ ----------
+ ticker: Stock Ticker
+ z : The variable for the Z axis
+ """
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("opt-oi %s %s", ticker, z)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ data = yfinance_model.get_iv_surface(ticker)
+ if data.empty:
+ raise Exception(f"No options data found for {ticker}.\n")
+
+ Y = data.dte
+ X = data.strike
+ if z == "IV":
+ Z = data.impliedVolatility
+ label = "Volatility"
+ elif z == "OI":
+ Z = data.openInterest
+ label = "Open Interest"
+ elif z == "LP":
+ Z = data.lastPrice
+ label = "Last Price"
+
+ points3D = np.vstack((X, Y, Z)).T
+ points2D = points3D[:, :2]
+ tri = Delaunay(points2D)
+ I, J, K = tri.simplices.T
+
+ lighting_effects = dict(
+ ambient=0.5, diffuse=0.5, roughness=0.5, specular=0.4, fresnel=0.4
+ )
+ fig = go.Figure(
+ data=[
+ go.Mesh3d(
+ z=Z,
+ x=X,
+ y=Y,
+ i=I,
+ j=J,
+ k=K,
+ intensity=Z,
+ colorscale=cfg.PLT_3DMESH_COLORSCALE,
+ hovertemplate="DTE: %{y}
Strike: %{x}
"
+ + z
+ + ": %{z}",
+ showscale=False,
+ flatshading=True,
+ lighting=lighting_effects,
+ )
+ ]
+ )
+ fig.update_layout(
+ scene=dict(
+ xaxis=dict(
+ title="Strike",
+ tickfont=dict(size=11),
+ titlefont=dict(size=12),
+ ),
+ yaxis=dict(
+ title="DTE",
+ ),
+ zaxis=dict(
+ title=z,
+ ),
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=40, b=20),
+ template=cfg.PLT_3DMESH_STYLE_TEMPLATE,
+ title=f"{label} Surface for {ticker.upper()}",
+ title_x=0.5,
+ hoverlabel=cfg.PLT_3DMESH_HOVERLABEL,
+ scene_camera=dict(
+ up=dict(x=0, y=0, z=2),
+ center=dict(x=0, y=0, z=-0.3),
+ eye=dict(x=1.25, y=1.25, z=0.69),
+ ),
+ scene=cfg.PLT_3DMESH_SCENE,
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "opt-vsurf.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/vsurf_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vsurf_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"{label} Surface for {ticker.upper()}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/quote.py b/bots/stocks/quote.py
new file mode 100644
index 00000000000..ff17138ef54
--- /dev/null
+++ b/bots/stocks/quote.py
@@ -0,0 +1,40 @@
+import df2img
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import quote, save_image
+
+
+def quote_command(ticker: str = None):
+ """Ticker Quote"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("quote %s", ticker)
+
+ # Check for argument
+ if ticker is None:
+ raise Exception("Stock ticker is required")
+
+ df = quote(ticker)
+ fig = df2img.plot_dataframe(
+ df,
+ fig_size=(600, 1500),
+ col_width=[2, 3],
+ tbl_cells=dict(
+ align="left",
+ height=35,
+ ),
+ template="plotly_dark",
+ font=dict(
+ family="Consolas",
+ size=20,
+ ),
+ paper_bgcolor="rgba(0, 0, 0, 0)",
+ )
+ imagefile = save_image("quote.png", fig)
+
+ return {
+ "title": f"{ticker.upper()} Quote",
+ "imagefile": imagefile,
+ }
diff --git a/discordbot/stocks/screener/__init__.py b/bots/stocks/screener/__init__.py
similarity index 100%
rename from discordbot/stocks/screener/__init__.py
rename to bots/stocks/screener/__init__.py
diff --git a/bots/stocks/screener/financial.py b/bots/stocks/screener/financial.py
new file mode 100644
index 00000000000..d079bc9bf2a
--- /dev/null
+++ b/bots/stocks/screener/financial.py
@@ -0,0 +1,125 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def financial_command(preset="template", sort="", limit: int = 5, ascend: bool = False):
+ """Displays returned results from preset by financial metrics [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-financial %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "financial",
+ limit,
+ ascend,
+ )
+
+ description = ""
+ title = "Stocks: [Finviz] Financial Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in so.d_cols_to_sort["financial"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ so.d_cols_to_sort["financial"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['financial'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = description + "Overview"
+ i = 1
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/screener/historical.py b/bots/stocks/screener/historical.py
new file mode 100644
index 00000000000..483d2e29bee
--- /dev/null
+++ b/bots/stocks/screener/historical.py
@@ -0,0 +1,137 @@
+import configparser
+import random
+from datetime import datetime, timedelta
+
+import yfinance as yf
+from finvizfinance.screener import ticker
+from matplotlib import pyplot as plt
+from pandas.plotting import register_matplotlib_converters
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+from gamestonk_terminal.stocks.screener import finviz_model
+
+# pylint:disable=no-member
+
+
+def historical_command(signal: str = "", start=""):
+ """Displays historical price comparison between similar companies [Yahoo Finance]"""
+
+ # Debug user input
+ if cfg.DEBUG:
+ logger.debug("scr-historical %s %s", signal, start)
+
+ # Check for argument
+ if signal not in so.d_signals_desc:
+ raise Exception("Invalid preset selected!")
+
+ register_matplotlib_converters()
+
+ screen = ticker.Ticker()
+
+ if signal in finviz_model.d_signals:
+ screen.set_filter(signal=finviz_model.d_signals[signal])
+
+ else:
+ preset_filter = configparser.RawConfigParser()
+ preset_filter.optionxform = str # type: ignore
+ preset_filter.read(so.presets_path + signal + ".ini")
+
+ d_general = preset_filter["General"]
+ d_filters = {
+ **preset_filter["Descriptive"],
+ **preset_filter["Fundamental"],
+ **preset_filter["Technical"],
+ }
+
+ d_filters = {k: v for k, v in d_filters.items() if v}
+
+ if d_general["Signal"]:
+ screen.set_filter(filters_dict=d_filters, signal=d_general["Signal"])
+ else:
+ screen.set_filter(filters_dict=d_filters)
+
+ if start == "":
+ start = datetime.now() - timedelta(days=365)
+ else:
+ start = datetime.strptime(start, cfg.DATE_FORMAT)
+
+ # Output Data
+ l_min = []
+ l_leg = []
+ l_stocks = screen.ScreenerView(verbose=0)
+
+ if len(l_stocks) > 10:
+ description = (
+ "\nThe limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed."
+ "\nThe selected list will be: "
+ )
+ random.shuffle(l_stocks)
+ l_stocks = sorted(l_stocks[:10])
+ description = description + (", ".join(l_stocks))
+ logger.debug(description)
+
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ while l_stocks:
+ l_parsed_stocks = []
+ for symbol in l_stocks:
+ try:
+ df_similar_stock = yf.download(
+ symbol,
+ start=datetime.strftime(start, "%Y-%m-%d"),
+ progress=False,
+ threads=False,
+ )
+ if not df_similar_stock.empty:
+ plt.plot(
+ df_similar_stock.index,
+ df_similar_stock["Adj Close"].values,
+ )
+ l_min.append(df_similar_stock.index[0])
+ l_leg.append(symbol)
+
+ l_parsed_stocks.append(symbol)
+ except Exception as e:
+ error = (
+ f"{e}\nDisregard previous error, which is due to API Rate limits from Yahoo Finance. "
+ f"Because we like '{symbol}', and we won't leave without getting data from it."
+ )
+
+ return {
+ "title": "ERROR Stocks: [Yahoo Finance] Historical Screener",
+ "description": error,
+ }
+
+ for parsed_stock in l_parsed_stocks:
+ l_stocks.remove(parsed_stock)
+
+ if signal:
+ plt.title(
+ f"Screener Historical Price using {finviz_model.d_signals[signal]} signal"
+ )
+ else:
+ plt.title(f"Screener Historical Price using {signal} preset")
+
+ plt.xlabel("Time")
+ plt.ylabel("Share Price ($)")
+ plt.legend(l_leg)
+ plt.grid(b=True, which="major", color="#666666", linestyle="-")
+ plt.minorticks_on()
+ plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
+ # ensures that the historical data starts from same datapoint
+ plt.xlim([max(l_min), df_similar_stock.index[-1]])
+
+ imagefile = "scr_historical.png"
+ plt.savefig(imagefile)
+ imagefile = image_border(imagefile)
+
+ return {
+ "title": "Stocks: [Yahoo Finance] Historical Screener",
+ "description": description,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/screener/overview.py b/bots/stocks/screener/overview.py
new file mode 100644
index 00000000000..3e92b3d2c5c
--- /dev/null
+++ b/bots/stocks/screener/overview.py
@@ -0,0 +1,128 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def overview_command(
+ preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
+):
+ """Displays stocks with overview data such as Sector and Industry [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-overview %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "overview",
+ limit,
+ ascend,
+ )
+
+ description = ""
+ title = "Stocks: [Finviz] Overview Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in so.d_cols_to_sort["overview"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ so.d_cols_to_sort["overview"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['overview'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = description + "Overview"
+ i = 1
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/screener/ownership.py b/bots/stocks/screener/ownership.py
new file mode 100644
index 00000000000..d20df410a3f
--- /dev/null
+++ b/bots/stocks/screener/ownership.py
@@ -0,0 +1,127 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def ownership_command(
+ preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
+):
+ """Displays stocks based on own share float and ownership data [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-ownership %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "ownership",
+ limit,
+ ascend,
+ )
+
+ description = ""
+ title = "Stocks: [Finviz] Ownership Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in so.d_cols_to_sort["ownership"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ so.d_cols_to_sort["ownership"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['ownership'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = description + "Overview"
+ i = 1
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/screener/performance.py b/bots/stocks/screener/performance.py
new file mode 100644
index 00000000000..8252b798a63
--- /dev/null
+++ b/bots/stocks/screener/performance.py
@@ -0,0 +1,148 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def performance_command(
+ preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
+):
+ """Displays stocks and sort by performance categories [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-performance %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "performance",
+ limit,
+ ascend,
+ )
+
+ d_cols_to_sort = {
+ "performance": [
+ "Ticker",
+ "Perf Week",
+ "Perf Month",
+ "Perf Quart",
+ "Perf Half",
+ "Perf Year",
+ "Perf YTD",
+ "Volatility W",
+ "Volatility M",
+ "Recom",
+ "Avg Volume",
+ "Rel Volume",
+ "Price",
+ "Change",
+ "Volume",
+ ],
+ }
+
+ description = ""
+ title = "Stocks: [Finviz] Performance Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in d_cols_to_sort["performance"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ d_cols_to_sort["performance"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ "Wrong sort column provided! Provide one of these:"
+ f"{', '.join(d_cols_to_sort['performance'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = description + "Overview"
+ i = 1
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/screener/presets_custom.py b/bots/stocks/screener/presets_custom.py
new file mode 100644
index 00000000000..6dd16960f44
--- /dev/null
+++ b/bots/stocks/screener/presets_custom.py
@@ -0,0 +1,29 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.stocks.screener import screener_options as so
+
+
+def presets_custom_command():
+ """Displays every custom preset"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-presets")
+
+ description = ""
+ for preset in so.presets:
+ with open(
+ so.presets_path + preset + ".ini",
+ encoding="utf8",
+ ) as f:
+ preset_line = ""
+ for line in f:
+ if line.strip() == "[General]":
+ break
+ preset_line += line.strip()
+ description += f"**{preset}:** *{preset_line.split('Description: ')[1].replace('#', '')}*\n"
+
+ return {
+ "title": "Stocks: Screener Custom Presets",
+ "description": description,
+ }
diff --git a/bots/stocks/screener/presets_default.py b/bots/stocks/screener/presets_default.py
new file mode 100644
index 00000000000..193138453b3
--- /dev/null
+++ b/bots/stocks/screener/presets_default.py
@@ -0,0 +1,20 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.stocks.screener import screener_options as so
+
+
+def presets_default_command():
+ """Displays default presets"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-presets")
+
+ description = ""
+ for signame, sigdesc in so.d_signals_desc.items():
+ description += f"**{signame}:** *{sigdesc}*\n"
+
+ return {
+ "title": "Stocks: Screener Default Presets",
+ "description": description,
+ }
diff --git a/discordbot/stocks/screener/screener_options.py b/bots/stocks/screener/screener_options.py
similarity index 99%
rename from discordbot/stocks/screener/screener_options.py
rename to bots/stocks/screener/screener_options.py
index 450eba73834..77eca2cb11a 100644
--- a/discordbot/stocks/screener/screener_options.py
+++ b/bots/stocks/screener/screener_options.py
@@ -1,6 +1,6 @@
import os
-import discordbot.config_discordbot as cfg
+import bots.config_discordbot as cfg
presets_path = os.path.join(
cfg.GST_PATH, "gamestonk_terminal", "stocks", "screener", "presets", ""
diff --git a/bots/stocks/screener/technical.py b/bots/stocks/screener/technical.py
new file mode 100644
index 00000000000..2eff5b247d8
--- /dev/null
+++ b/bots/stocks/screener/technical.py
@@ -0,0 +1,127 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def technical_command(
+ preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
+):
+ """Displays stocks according to chosen preset, sorting by technical factors [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-technical %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "technical",
+ limit,
+ ascend,
+ )
+
+ description = ""
+ title = "Stocks: [Finviz] Technical Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in so.d_cols_to_sort["technical"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ so.d_cols_to_sort["technical"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['technical'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ initial_str = description + "Overview"
+ i = 1
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/bots/stocks/screener/valuation.py b/bots/stocks/screener/valuation.py
new file mode 100644
index 00000000000..5e28d20c212
--- /dev/null
+++ b/bots/stocks/screener/valuation.py
@@ -0,0 +1,127 @@
+import difflib
+
+import disnake
+import pandas as pd
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.menus.menu import Menu
+from bots.stocks.screener import screener_options as so
+from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
+
+
+def valuation_command(
+ preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
+):
+ """Displays results from chosen preset focusing on valuation metrics [Finviz]"""
+
+ # Check for argument
+ if preset == "template" or preset not in so.all_presets:
+ raise Exception("Invalid preset selected!")
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("scr-valuation %s %s %s %s", preset, sort, limit, ascend)
+
+ # Check for argument
+ if limit < 0:
+ raise Exception("Number has to be above 0")
+
+ # Output Data
+ df_screen = get_screener_data(
+ preset,
+ "valuation",
+ limit,
+ ascend,
+ )
+
+ description = ""
+ title = "Stocks: [Finviz] Valuation Screener"
+ if isinstance(df_screen, pd.DataFrame):
+ if df_screen.empty:
+ raise Exception("No data found.")
+
+ df_screen = df_screen.dropna(axis="columns", how="all")
+
+ if sort:
+ if " ".join(sort) in so.d_cols_to_sort["valuation"]:
+ df_screen = df_screen.sort_values(
+ by=[" ".join(sort)],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ similar_cmd = difflib.get_close_matches(
+ " ".join(sort),
+ so.d_cols_to_sort["valuation"],
+ n=1,
+ cutoff=0.7,
+ )
+ if similar_cmd:
+ description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
+ df_screen = df_screen.sort_values(
+ by=[similar_cmd[0]],
+ ascending=ascend,
+ na_position="last",
+ )
+ else:
+ raise ValueError(
+ f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['valuation'])}"
+ )
+
+ df_screen = df_screen.fillna("")
+ future_column_name = df_screen["Ticker"]
+ df_screen = df_screen.head(n=limit).transpose()
+ df_screen.columns = future_column_name
+ df_screen.drop("Ticker")
+
+ embeds = []
+ choices = [
+ disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
+ ]
+ initial_str = description + "Overview"
+ i = 1
+ for column in df_screen.columns.values:
+ menu = f"\nPage {i}: {column}"
+ initial_str += f"\nPage {i}: {column}"
+ if i < 19:
+ choices.append(
+ disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
+ )
+ if i == 20:
+ choices.append(
+ disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
+ )
+ i += 1
+ reports = [f"{initial_str}"]
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=initial_str,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ for column in df_screen.columns.values:
+ description = f"```{df_screen[column].fillna('')}```"
+ embeds.append(
+ disnake.Embed(
+ title=title,
+ description=description,
+ colour=cfg.COLOR,
+ ).set_author(
+ name=cfg.AUTHOR_NAME,
+ icon_url=cfg.AUTHOR_ICON_URL,
+ )
+ )
+ reports.append(f"{description}")
+
+ return {
+ "view": Menu,
+ "title": title,
+ "description": reports,
+ "embed": embeds,
+ "choices": choices,
+ }
diff --git a/discordbot/stocks/technical_analysis/__init__.py b/bots/stocks/technical_analysis/__init__.py
similarity index 100%
rename from discordbot/stocks/technical_analysis/__init__.py
rename to bots/stocks/technical_analysis/__init__.py
diff --git a/bots/stocks/technical_analysis/ad.py b/bots/stocks/technical_analysis/ad.py
new file mode 100644
index 00000000000..57012fccd45
--- /dev/null
+++ b/bots/stocks/technical_analysis/ad.py
@@ -0,0 +1,150 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import volume_model
+
+
+def ad_command(ticker="", is_open="False", start="", end=""):
+ """Displays chart with accumulation/distribution line [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("ta-ad %s %s %s %s", ticker, is_open, start, end)
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if is_open in ["false", "False", "FALSE", ""]:
+ is_open = False
+
+ if is_open in ["true", "True", "TRUE"]:
+ is_open = True
+
+ if is_open not in [True, False]:
+ raise Exception("is_open argument has to be true or false")
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ divisor = 1_000_000
+ df_vol = df_stock["Volume"].dropna()
+ df_vol = df_vol.values / divisor
+ df_ta = volume_model.ad(df_stock, is_open)
+ df_cal = df_ta.values
+ df_cal = df_cal / divisor
+
+ fig = make_subplots(
+ rows=3,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.06,
+ row_width=[0.2, 0.2, 0.2],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ row=1,
+ col=1,
+ )
+ colors = [
+ "green" if row.Open < row["Adj Close"] else "red"
+ for _, row in df_stock.iterrows()
+ ]
+ fig.add_trace(
+ go.Bar(
+ x=df_stock.index,
+ y=df_vol,
+ name="Volume [M]",
+ marker_color=colors,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="A/D [M]",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ line=dict(width=2),
+ opacity=1,
+ ),
+ row=3,
+ col=1,
+ )
+ fig.add_hline(
+ y=0,
+ fillcolor="grey",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="grey", dash="dash"),
+ row=3,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=10, r=0, t=40, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} AD",
+ title_x=0.4,
+ yaxis_title="Stock Price ($)",
+ yaxis2_title="Volume [M]",
+ yaxis3_title="A/D [M]",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_ad.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/ad_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ad_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Accumulation/Distribution Line {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/adosc.py b/bots/stocks/technical_analysis/adosc.py
new file mode 100644
index 00000000000..e206751351d
--- /dev/null
+++ b/bots/stocks/technical_analysis/adosc.py
@@ -0,0 +1,145 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import volume_model
+
+
+def adosc_command(ticker="", is_open="False", fast="3", slow="10", start="", end=""):
+ """Displays chart with chaikin oscillator [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-adosc %s %s %s %s %s",
+ ticker,
+ is_open,
+ fast,
+ slow,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not fast.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ fast = float(fast)
+ if not slow.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ slow = float(slow)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ # Output Data
+ divisor = 1_000_000
+ df_vol = df_stock["Volume"].dropna()
+ df_vol = df_vol.values / divisor
+ df_ta = volume_model.adosc(df_stock, is_open, fast, slow)
+ df_cal = df_ta.values
+ df_cal = df_cal / divisor
+
+ fig = make_subplots(
+ rows=3,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.06,
+ row_width=[0.2, 0.2, 0.2],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ row=1,
+ col=1,
+ )
+ colors = [
+ "green" if row.Open < row["Adj Close"] else "red"
+ for _, row in df_stock.iterrows()
+ ]
+ fig.add_trace(
+ go.Bar(
+ x=df_stock.index,
+ y=df_vol,
+ name="Volume",
+ marker_color=colors,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="AD Osc [M]",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ line=dict(width=2),
+ opacity=1,
+ ),
+ row=3,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=10, r=0, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} AD Oscillator",
+ title_x=0.3,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_adosc.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/adosc_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/adosc_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Accumulation/Distribution Oscillator {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/adx.py b/bots/stocks/technical_analysis/adx.py
new file mode 100644
index 00000000000..3585fea17d8
--- /dev/null
+++ b/bots/stocks/technical_analysis/adx.py
@@ -0,0 +1,176 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import trend_indicators_model
+
+
+def adx_command(ticker="", length="14", scalar="100", drift="1", start="", end=""):
+ """Displays chart with average directional movement index [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-adx %s %s %s %s %s",
+ ticker,
+ length,
+ scalar,
+ drift,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = float(length)
+ if not scalar.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ scalar = float(scalar)
+ if not drift.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ drift = float(drift)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = trend_indicators_model.adx(
+ df_stock["High"],
+ df_stock["Low"],
+ df_stock["Adj Close"],
+ length,
+ scalar,
+ drift,
+ )
+
+ # Output Data
+ leg_adx = df_ta.columns[0].replace("_", " ")
+ neg_di = df_ta.columns[1].replace("_", " ")
+ pos_di = df_ta.columns[2].replace("_", " ")
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.5],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ row=1,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ mode="lines",
+ name=f"ADX ({leg_adx})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ line=dict(width=2),
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ mode="lines",
+ name=f"+DI ({pos_di})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ line=dict(width=1),
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ mode="lines",
+ name=f"- DI ({neg_di})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 2].values,
+ opacity=1,
+ line=dict(width=1),
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=25,
+ fillcolor="grey",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="grey", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Average Directional Movement Index (ADX) on {ticker}",
+ title_x=0.3,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_adx.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/adx_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/adx_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Average-Directional-Movement-Index {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/aroon.py b/bots/stocks/technical_analysis/aroon.py
new file mode 100644
index 00000000000..b7a8a559f92
--- /dev/null
+++ b/bots/stocks/technical_analysis/aroon.py
@@ -0,0 +1,160 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import trend_indicators_model
+
+
+def aroon_command(ticker="", length="25", scalar="100", start="", end=""):
+ """Displays chart with aroon indicator [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-aroon %s %s %s %s %s",
+ ticker,
+ length,
+ scalar,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = int(length)
+ if not scalar.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ scalar = float(scalar)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = trend_indicators_model.aroon(
+ df_stock["High"], df_stock["Low"], length, scalar
+ )
+
+ # Output Data
+ aadown = df_ta.columns[0].replace("_", " ")
+ aaup = df_ta.columns[1].replace("_", " ")
+ aaosc = df_ta.columns[2].replace("_", " ")
+
+ fig = make_subplots(
+ rows=3,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.2, 0.2, 0.2],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"Aroon DOWN ({aadown})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"Aroon UP ({aaup})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"Aroon OSC ({aaosc})",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 2].values,
+ opacity=1,
+ ),
+ row=3,
+ col=1,
+ )
+ fig.add_hline(
+ y=50,
+ fillcolor="grey",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="grey", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Aroon on {ticker}",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_aroon.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/aroon_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/aroon_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Aroon-Indicator {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/bbands.py b/bots/stocks/technical_analysis/bbands.py
new file mode 100644
index 00000000000..77eee908bf5
--- /dev/null
+++ b/bots/stocks/technical_analysis/bbands.py
@@ -0,0 +1,169 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import volatility_model
+
+
+def bbands_command(ticker="", length="5", n_std="2", mamode="sma", start="", end=""):
+ """Displays chart with bollinger bands [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-bbands %s %s %s %s %s %s",
+ ticker,
+ length,
+ n_std,
+ mamode,
+ start,
+ end,
+ )
+
+ # Check for argument
+ possible_ma = [
+ "dema",
+ "ema",
+ "fwma",
+ "hma",
+ "linreg",
+ "midpoint",
+ "pwma",
+ "rma",
+ "sinwma",
+ "sma",
+ "swma",
+ "t3",
+ "tema",
+ "trima",
+ "vidya",
+ "wma",
+ "zlma",
+ ]
+
+ 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = float(length)
+ if not n_std.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ n_std = float(n_std)
+
+ if mamode not in possible_ma:
+ raise Exception("Invalid ma entered")
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = volatility_model.bbands(df_stock["Adj Close"], length, n_std, mamode)
+
+ # Output Data
+ bbu = df_ta.columns[2].replace("_", " ")
+ bbm = df_ta.columns[1].replace("_", " ")
+ bbl = df_ta.columns[0].replace("_", " ")
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{bbu}",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 2].values,
+ opacity=1,
+ mode="lines",
+ line_color="indigo",
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"{bbl}",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ mode="lines",
+ line_color="indigo",
+ fill="tonexty",
+ fillcolor="rgba(74, 0, 128, 0.2)",
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"{bbm}",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ line=dict(
+ width=1.5,
+ dash="dash",
+ ),
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=df_stock.index,
+ y=df_stock["Adj Close"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} Bollinger Bands ({mamode.upper()})",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_bbands.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/bbands_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/bbands_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Bollinger-Bands {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/cci.py b/bots/stocks/technical_analysis/cci.py
new file mode 100644
index 00000000000..7bb8126b735
--- /dev/null
+++ b/bots/stocks/technical_analysis/cci.py
@@ -0,0 +1,170 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import momentum_model
+
+
+def cci_command(ticker="", length="14", scalar="0.015", start="", end=""):
+ """Displays chart with commodity channel index [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-cci %s %s %s %s %s",
+ ticker,
+ length,
+ scalar,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+ # pylint
+ try:
+ length = int(length)
+ except ValueError as e:
+ raise Exception("Length has to be an integer") from e
+ try:
+ scalar = float(scalar)
+ except ValueError as e:
+ raise Exception("Scalar has to be an integer") from e
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+ df_ta = momentum_model.cci(
+ df_stock["High"], df_stock["Low"], df_stock["Adj Close"], length, scalar
+ )
+
+ dmin = df_ta.values.min()
+ dmax = df_ta.values.max()
+ # Output Data
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.6],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ showlegend=False,
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=100,
+ y1=dmax,
+ fillcolor="red",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=-100,
+ y1=dmin,
+ fillcolor="green",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=-100,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="green", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=100,
+ fillcolor="red",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="red", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} CCI",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_cci.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/cci_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cci_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Commodity-Channel-Index {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/cg.py b/bots/stocks/technical_analysis/cg.py
new file mode 100644
index 00000000000..77866d3333b
--- /dev/null
+++ b/bots/stocks/technical_analysis/cg.py
@@ -0,0 +1,86 @@
+from datetime import datetime, timedelta
+
+import numpy as np
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+import bots.helpers
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.common.technical_analysis import momentum_model
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+
+
+def cg_command(ticker="", length="14", start="", end=""):
+ """Displays chart with centre of gravity [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-cg %s %s %s %s",
+ ticker,
+ length,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = float(length)
+
+ ticker = ticker.upper()
+ df_stock = bots.helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+ df_close = df_stock["Adj Close"]
+ df_close.columns = ["values"]
+ df_ta = momentum_model.cg(df_close, length)
+
+ # Output Data
+ fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI)
+ ax = axes[0]
+ ax.set_title(f"{ticker} Centre of Gravity")
+ ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=1)
+ ax.set_xlim(df_stock.index[0], df_stock.index[-1])
+ ax.set_ylabel("Share Price ($)")
+ ax.grid(b=True, which="major", color="#666666", linestyle="-")
+
+ ax2 = axes[1]
+ ax2.plot(df_ta.index, df_ta.values, "b", lw=2, label="CG")
+ # shift cg 1 bar forward for signal
+ signal = df_ta.values
+ signal = np.roll(signal, 1)
+ ax2.plot(df_ta.index, signal, "g", lw=1, label="Signal")
+ ax2.set_xlim(df_stock.index[0], df_stock.index[-1])
+ ax2.grid(b=True, which="major", color="#666666", linestyle="-")
+
+ plt.gcf().autofmt_xdate()
+ fig.tight_layout(pad=1)
+ plt.legend()
+ imagefile = "ta_cg.png"
+ plt.savefig(imagefile)
+
+ imagefile = image_border(imagefile)
+
+ return {
+ "title": f"Stocks: Center-of-Gravity {ticker}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/donchian.py b/bots/stocks/technical_analysis/donchian.py
new file mode 100644
index 00000000000..6100c28f2d4
--- /dev/null
+++ b/bots/stocks/technical_analysis/donchian.py
@@ -0,0 +1,96 @@
+from datetime import datetime, timedelta
+
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+import bots.helpers
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.common.technical_analysis import volatility_model
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import plot_autoscale
+
+
+def donchian_command(
+ ticker="", upper_length="25", lower_length="100", start="", end=""
+):
+ """Displays chart with donchian channel [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta donchian %s %s %s %s %s",
+ ticker,
+ upper_length,
+ lower_length,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not upper_length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ upper_length = float(upper_length)
+ if not lower_length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ lower_length = float(lower_length)
+
+ ticker = ticker.upper()
+ df_stock = bots.helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = volatility_model.donchian(
+ df_stock["High"], df_stock["Low"], upper_length, lower_length
+ )
+
+ # Output Data
+ fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ ax.plot(df_stock.index, df_stock["Adj Close"].values, color="k", lw=3)
+ ax.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=1.5, label="upper")
+ ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--")
+ ax.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=1.5, label="lower")
+ ax.set_title(f"{ticker} donchian")
+ ax.set_xlim(df_stock.index[0], df_stock.index[-1])
+ ax.set_xlabel("Time")
+ ax.set_ylabel("Price ($)")
+
+ ax.legend([ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]])
+ ax.fill_between(
+ df_ta.index,
+ df_ta.iloc[:, 0].values,
+ df_ta.iloc[:, 2].values,
+ alpha=0.1,
+ color="b",
+ )
+ ax.grid(b=True, which="major", color="#666666", linestyle="-")
+
+ plt.gcf().autofmt_xdate()
+ fig.tight_layout(pad=1)
+
+ plt.legend()
+ imagefile = "ta_donchian.png"
+ plt.savefig(imagefile)
+
+ imagefile = image_border(imagefile)
+
+ return {
+ "title": f"Stocks: Donchian-Channels {ticker}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/ema.py b/bots/stocks/technical_analysis/ema.py
new file mode 100644
index 00000000000..73e8f43d125
--- /dev/null
+++ b/bots/stocks/technical_analysis/ema.py
@@ -0,0 +1,139 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def ema_command(ticker="", window="", offset="", start="", end=""):
+ """Displays chart with exponential moving average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-ema %s %s %s %s %s",
+ ticker,
+ window,
+ offset,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ l_legend = [ticker]
+
+ if window == "":
+ window = [20, 50]
+ else:
+ window_temp = list()
+ for wind in window.split(","):
+ try:
+ window_temp.append(float(wind))
+ except Exception as e:
+ raise Exception("Window needs to be a float") from e
+ window = window_temp
+
+ if offset == "":
+ offset = 0
+
+ ticker = ticker.upper()
+ stock = helpers.load(ticker, start)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ price_df = pd.DataFrame(
+ stock["Adj Close"].values, columns=["Price"], index=stock.index
+ )
+
+ i = 1
+ for win in window:
+ ema_data = overlap_model.ema(
+ values=stock["Adj Close"], length=win, offset=offset
+ )
+ price_df = price_df.join(ema_data)
+ l_legend.append(f"EMA {win}")
+ i += 1
+
+ # Output Data
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=price_df.index,
+ y=price_df["Price"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ for i in range(1, price_df.shape[1]):
+ trace_name = price_df.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=price_df.index,
+ y=price_df.iloc[:, i],
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} EMA",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_ema.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/ema_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ema_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Exponential-Moving-Average {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/fib.py b/bots/stocks/technical_analysis/fib.py
new file mode 100644
index 00000000000..3efd705cf05
--- /dev/null
+++ b/bots/stocks/technical_analysis/fib.py
@@ -0,0 +1,93 @@
+from datetime import datetime, timedelta
+
+from matplotlib import pyplot as plt
+
+import bots.config_discordbot as cfg
+import bots.helpers
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal import config_plot as cfp
+from gamestonk_terminal.common.technical_analysis import custom_indicators_model
+from gamestonk_terminal.helper_funcs import plot_autoscale
+
+
+def fib_command(ticker="", start="", end=""):
+ """Displays chart with fibonacci retracement [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-fib %s %s %s",
+ ticker,
+ start,
+ end,
+ )
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ if start == "":
+ start = datetime.now() - timedelta(days=365)
+ f_start = None
+ else:
+ start = datetime.strptime(start, cfg.DATE_FORMAT)
+ f_start = start
+
+ if end == "":
+ end = datetime.now()
+ f_end = None
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+ f_end = None
+
+ ticker = ticker.upper()
+ df_stock = bots.helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ (
+ df_fib,
+ min_date,
+ max_date,
+ min_pr,
+ max_pr,
+ ) = custom_indicators_model.calculate_fib_levels(df_stock, 120, f_start, f_end)
+
+ levels = df_fib.Price
+ fig, ax = plt.subplots(figsize=(plot_autoscale()), dpi=cfp.PLOT_DPI)
+
+ ax.plot(df_stock["Adj Close"], "b")
+ ax.plot([min_date, max_date], [min_pr, max_pr], c="k")
+
+ for i in levels:
+ ax.axhline(y=i, c="g", alpha=0.5)
+
+ for i in range(5):
+ ax.fill_between(df_stock.index, levels[i], levels[i + 1], alpha=0.6)
+
+ ax.set_ylabel("Price")
+ ax.set_title(f"Fibonacci Support for {ticker.upper()}")
+ ax.set_xlim(df_stock.index[0], df_stock.index[-1])
+
+ ax1 = ax.twinx()
+ ax1.set_ylim(ax.get_ylim())
+ ax1.set_yticks(levels)
+ ax1.set_yticklabels([0, 0.235, 0.382, 0.5, 0.618, 1])
+
+ plt.gcf().autofmt_xdate()
+ fig.tight_layout(pad=1)
+ imagefile = "ta_fib.png"
+ plt.savefig(imagefile)
+
+ imagefile = image_border(imagefile)
+
+ return {
+ "title": f"Stocks: Fibonacci-Retracement-Levels {ticker}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/fisher.py b/bots/stocks/technical_analysis/fisher.py
new file mode 100644
index 00000000000..0378b217543
--- /dev/null
+++ b/bots/stocks/technical_analysis/fisher.py
@@ -0,0 +1,167 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import momentum_model
+
+
+def fisher_command(ticker="", length="14", start="", end=""):
+ """Displays chart with fisher transformation [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("ta-fisher %s %s %s %s", ticker, length, start, end)
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = int(length)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = momentum_model.fisher(df_stock["High"], df_stock["Low"], length)
+ dmin = df_ta.values.min()
+ dmax = df_ta.values.max()
+ # Output Data
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.6],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="Fisher",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="Signal",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=2,
+ y1=dmax,
+ fillcolor="red",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=-2,
+ y1=dmin,
+ fillcolor="green",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=-2,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="green", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=2,
+ fillcolor="red",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="red", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} Fisher Transform",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_fisher.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/fisher_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/fisher_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Fisher-Transform {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/hma.py b/bots/stocks/technical_analysis/hma.py
new file mode 100644
index 00000000000..a7d95e82e65
--- /dev/null
+++ b/bots/stocks/technical_analysis/hma.py
@@ -0,0 +1,134 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def hma_command(ticker="", window="", offset="", start="", end=""):
+ """Displays chart with hull moving average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-hma %s %s %s %s %s",
+ ticker,
+ window,
+ offset,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ l_legend = [ticker]
+
+ if window == "":
+ window = [20, 50]
+ else:
+ window_temp = list()
+ for wind in window.split(","):
+ try:
+ window_temp.append(float(wind))
+ except Exception as e:
+ raise Exception("Window needs to be a float") from e
+ window = window_temp
+
+ ticker = ticker.upper()
+ stock = helpers.load(ticker, start)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ price_df = pd.DataFrame(
+ stock["Adj Close"].values, columns=["Price"], index=stock.index
+ )
+ i = 1
+ for win in window:
+ hma_data = overlap_model.hma(
+ values=stock["Adj Close"], length=win, offset=offset
+ )
+ price_df = price_df.join(hma_data)
+ l_legend.append(f"HMA {win}")
+ i += 1
+
+ # Output Data
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=price_df.index,
+ y=price_df["Price"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ for i in range(1, price_df.shape[1]):
+ trace_name = price_df.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=price_df.index,
+ y=price_df.iloc[:, i],
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} HMA",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_hma.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/hma_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hma_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Hull-Moving-Average {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/kc.py b/bots/stocks/technical_analysis/kc.py
new file mode 100644
index 00000000000..8bce979b139
--- /dev/null
+++ b/bots/stocks/technical_analysis/kc.py
@@ -0,0 +1,163 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import volatility_model
+
+
+def kc_command(
+ ticker="", length="20", scalar="2", mamode="sma", offset="0", start="", end=""
+):
+ """Displays chart with keltner channel [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-kc %s %s %s %s %s %s %s",
+ ticker,
+ length,
+ scalar,
+ mamode,
+ offset,
+ start,
+ end,
+ )
+
+ # Check for argument
+ possible_ma = ["sma", "ema", "wma", "hma", "zlma"]
+
+ 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = int(length)
+ if not scalar.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ scalar = float(scalar)
+ if not offset.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ offset = float(offset)
+
+ if mamode not in possible_ma:
+ raise Exception("Invalid ma entered")
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = volatility_model.kc(
+ df_stock["High"],
+ df_stock["Low"],
+ df_stock["Adj Close"],
+ length,
+ scalar,
+ mamode,
+ offset,
+ )
+
+ # Output Data
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name="upper",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 2].values,
+ opacity=1,
+ mode="lines",
+ line_color="indigo",
+ showlegend=False,
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="lower",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ mode="lines",
+ line_color="indigo",
+ fill="tonexty",
+ fillcolor="rgba(74, 0, 128, 0.2)",
+ showlegend=False,
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="mid",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ line=dict(
+ width=1.5,
+ dash="dash",
+ ),
+ ),
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=df_stock.index,
+ y=df_stock["Adj Close"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} Keltner Channels ({mamode.upper()})",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_kc.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/kc_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/kc_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Keltner-Channel {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/macd.py b/bots/stocks/technical_analysis/macd.py
new file mode 100644
index 00000000000..2268fe28e09
--- /dev/null
+++ b/bots/stocks/technical_analysis/macd.py
@@ -0,0 +1,152 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import momentum_model
+
+
+def macd_command(ticker="", fast="12", slow="26", signal="9", start="", end=""):
+ """Displays chart with moving average convergence/divergence [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-macd %s %s %s %s %s %s",
+ ticker,
+ fast,
+ slow,
+ signal,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not fast.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ fast = float(fast)
+ if not slow.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ slow = float(slow)
+ if not signal.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ signal = float(signal)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = momentum_model.macd(df_stock["Adj Close"], fast, slow, signal)
+ trace_name = df_ta.columns[0].replace("_", " ")
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.6],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ fig.add_trace(
+ go.Bar(
+ name="MACD Histogram",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 1].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ mode="lines",
+ name="MACD Line",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ mode="lines",
+ name="Signal Line",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 2].values,
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} {trace_name}",
+ title_x=0.3,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_macd.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/macd_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/macd_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Moving-Average-Convergence-Divergence {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/obv.py b/bots/stocks/technical_analysis/obv.py
new file mode 100644
index 00000000000..e441dd06ca0
--- /dev/null
+++ b/bots/stocks/technical_analysis/obv.py
@@ -0,0 +1,136 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import volume_model
+
+
+def obv_command(ticker="", start="", end=""):
+ """Displays chart with on balance volume [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-obv %s %s %s",
+ ticker,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ # Output Data
+ divisor = 1_000_000
+ df_vol = df_stock["Volume"].dropna()
+ df_vol = df_vol.values / divisor
+ df_ta = volume_model.obv(df_stock)
+ df_cal = df_ta.values
+ df_cal = df_cal / divisor
+
+ fig = make_subplots(
+ rows=3,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.06,
+ row_width=[0.2, 0.2, 0.2],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ row=1,
+ col=1,
+ )
+ colors = [
+ "green" if row.Open < row["Adj Close"] else "red"
+ for _, row in df_stock.iterrows()
+ ]
+ fig.add_trace(
+ go.Bar(
+ x=df_stock.index,
+ y=df_vol,
+ name="Volume [M]",
+ marker_color=colors,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name="OBC [M]",
+ x=df_ta.index,
+ y=df_ta.iloc[:, 0].values,
+ line=dict(width=2),
+ opacity=1,
+ ),
+ row=3,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=10, r=0, t=40, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} OBV",
+ title_x=0.4,
+ yaxis_title="Stock Price ($)",
+ yaxis2_title="Volume [M]",
+ yaxis3_title="OBV [M]",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_obv.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/obv_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/obv_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: On-Balance-Volume {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/recom.py b/bots/stocks/technical_analysis/recom.py
new file mode 100644
index 00000000000..bb4747b7038
--- /dev/null
+++ b/bots/stocks/technical_analysis/recom.py
@@ -0,0 +1,26 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.stocks.technical_analysis import tradingview_model
+
+
+def recom_command(ticker=""):
+ """Displays text of a given stocks recommendation based on ta [Tradingview API]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("ta-recom %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ recom = tradingview_model.get_tradingview_recommendation(ticker, "america", "", "")
+
+ recom = recom[["BUY", "NEUTRAL", "SELL", "RECOMMENDATION"]]
+
+ report = "```" + recom.to_string() + "```"
+
+ return {
+ "title": "Stocks: [Tradingview API] Recommendation based on TA",
+ "description": report,
+ }
diff --git a/bots/stocks/technical_analysis/rsi.py b/bots/stocks/technical_analysis/rsi.py
new file mode 100644
index 00000000000..26bfc09fbe9
--- /dev/null
+++ b/bots/stocks/technical_analysis/rsi.py
@@ -0,0 +1,171 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import momentum_model
+
+
+def rsi_command(ticker="", length="14", scalar="100", drift="1", start="", end=""):
+ """Displays chart with relative strength index [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ # pylint: disable=logging-too-many-args
+ logger.debug(
+ "ta-rsi %s %s %s %s %s %s",
+ ticker,
+ length,
+ scalar,
+ drift,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not length.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ length = float(length)
+ if not scalar.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ scalar = float(scalar)
+ if not drift.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ drift = float(drift)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+ df_ta = momentum_model.rsi(df_stock["Adj Close"], length, scalar, drift)
+
+ # Output Data
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.6],
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=ticker,
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ for i in range(0, df_ta.shape[1]):
+ trace_name = df_ta.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=df_ta.index,
+ y=df_ta.iloc[:, i],
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=70,
+ y1=100,
+ fillcolor="red",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=0,
+ y1=30,
+ fillcolor="green",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=70,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="red", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=30,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="green", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=20, t=30, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} {trace_name}",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_rsi.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/rsi_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/rsi_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Relative-Strength-Index {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/sma.py b/bots/stocks/technical_analysis/sma.py
new file mode 100644
index 00000000000..b459f7d5136
--- /dev/null
+++ b/bots/stocks/technical_analysis/sma.py
@@ -0,0 +1,135 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def sma_command(ticker="", window="", offset="", start="", end=""):
+ """Displays chart with simple moving average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-sma %s %s %s %s %s",
+ ticker,
+ window,
+ offset,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ l_legend = [ticker]
+
+ if window == "":
+ window = [20, 50]
+ else:
+ window_temp = list()
+ for wind in window.split(","):
+ try:
+ window_temp.append(float(wind))
+ except Exception as e:
+ raise Exception("Window needs to be a float") from e
+ window = window_temp
+
+ ticker = ticker.upper()
+ stock = helpers.load(ticker, start)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ price_df = pd.DataFrame(
+ stock["Adj Close"].values, columns=["Price"], index=stock.index
+ )
+ i = 1
+ for win in window:
+ sma_data = overlap_model.sma(
+ values=stock["Adj Close"], length=win, offset=offset
+ )
+ price_df = price_df.join(sma_data)
+ l_legend.append(f"SMA {win}")
+ i += 1
+
+ # Output Data
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=price_df.index,
+ y=price_df["Price"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ for i in range(1, price_df.shape[1]):
+ trace_name = price_df.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=price_df.index,
+ y=price_df.iloc[:, i],
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} SMA",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_sma.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/sma_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/sma_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Simple-Moving-Average {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/stoch.py b/bots/stocks/technical_analysis/stoch.py
new file mode 100644
index 00000000000..bafa48e4aac
--- /dev/null
+++ b/bots/stocks/technical_analysis/stoch.py
@@ -0,0 +1,188 @@
+from datetime import datetime, timedelta
+
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import momentum_model
+
+
+def stoch_command(ticker="", fast_k="14", slow_d="3", slow_k="3", start="", end=""):
+ """Displays chart with stochastic relative strength average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-stoch %s %s %s %s %s %s",
+ ticker,
+ fast_k,
+ slow_k,
+ slow_d,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ if not fast_k.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ fast_k = int(fast_k)
+ if not slow_k.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ slow_k = int(slow_k)
+ if not slow_d.lstrip("-").isnumeric():
+ raise Exception("Number has to be an integer")
+ slow_d = int(slow_d)
+
+ ticker = ticker.upper()
+ df_stock = helpers.load(ticker, start)
+ if df_stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
+
+ df_ta = momentum_model.stoch(
+ df_stock["High"],
+ df_stock["Low"],
+ df_stock["Adj Close"],
+ fast_k,
+ slow_d,
+ slow_k,
+ )
+
+ fig = make_subplots(
+ rows=2,
+ cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.07,
+ row_width=[0.5, 0.7],
+ )
+ fig.add_trace(
+ go.Scatter(
+ x=df_stock.index,
+ y=df_stock["Adj Close"].values,
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ showlegend=False,
+ ),
+ row=1,
+ col=1,
+ )
+ K = df_ta.columns[0].replace("_", " ")
+ D = df_ta.columns[1].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=f"%K {K}",
+ x=df_stock.index,
+ y=df_ta.iloc[:, 0].values,
+ line=dict(width=1.8),
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_trace(
+ go.Scatter(
+ name=f"%D {D}",
+ x=df_stock.index,
+ y=df_ta.iloc[:, 1].values,
+ line=dict(width=1.8, dash="dash"),
+ opacity=1,
+ ),
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=80,
+ y1=100,
+ fillcolor="red",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hrect(
+ y0=0,
+ y1=20,
+ fillcolor="green",
+ opacity=0.2,
+ layer="below",
+ line_width=0,
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=80,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="red", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.add_hline(
+ y=20,
+ fillcolor="green",
+ opacity=1,
+ layer="below",
+ line_width=3,
+ line=dict(color="green", dash="dash"),
+ row=2,
+ col=1,
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=40, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"Stochastic Relative Strength Index (STOCH RSI) on {ticker}",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_stoch.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/stoch_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/stoch_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Stochastic-Relative-Strength-Index {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/summary.py b/bots/stocks/technical_analysis/summary.py
new file mode 100644
index 00000000000..f0fb77fea01
--- /dev/null
+++ b/bots/stocks/technical_analysis/summary.py
@@ -0,0 +1,24 @@
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from gamestonk_terminal.stocks.technical_analysis import finbrain_model
+
+
+def summary_command(ticker=""):
+ """Displays text of a given stocks ta summary [FinBrain API]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("ta-summary %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ report = finbrain_model.get_technical_summary_report(ticker)
+ if report:
+ report = "```" + report.replace(". ", ".\n") + "```"
+
+ return {
+ "title": "Stocks: [FinBrain API] Summary",
+ "description": report,
+ }
diff --git a/bots/stocks/technical_analysis/view.py b/bots/stocks/technical_analysis/view.py
new file mode 100644
index 00000000000..dbda69e9268
--- /dev/null
+++ b/bots/stocks/technical_analysis/view.py
@@ -0,0 +1,33 @@
+import io
+
+from PIL import Image
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots.helpers import image_border
+from gamestonk_terminal.stocks.technical_analysis import finviz_model
+
+
+def view_command(ticker=""):
+ """Displays image from Finviz [Finviz]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug("ta-view %s", ticker)
+
+ # Check for argument
+ if ticker == "":
+ raise Exception("Stock ticker is required")
+
+ image_data = finviz_model.get_finviz_image(ticker)
+ dataBytesIO = io.BytesIO(image_data)
+ im = Image.open(dataBytesIO)
+ im = im.resize((800, 340), Image.ANTIALIAS)
+ imagefile = "ta_view.png"
+
+ im.save(imagefile, "PNG", quality=100)
+ imagefile = image_border(imagefile)
+
+ return {
+ "title": f"Stocks: [Finviz] Trendlines & Data {ticker}",
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/wma.py b/bots/stocks/technical_analysis/wma.py
new file mode 100644
index 00000000000..e5c3ac7ab38
--- /dev/null
+++ b/bots/stocks/technical_analysis/wma.py
@@ -0,0 +1,138 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def wma_command(ticker="", window="", offset="", start="", end=""):
+ """Displays chart with weighted moving average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-wma %s %s %s %s %s",
+ ticker,
+ window,
+ offset,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ l_legend = [ticker]
+
+ if window == "":
+ window = [20, 50]
+ else:
+ window_temp = list()
+ for wind in window.split(","):
+ try:
+ window_temp.append(float(wind))
+ except Exception as e:
+ raise Exception("Window needs to be a float") from e
+ window = window_temp
+
+ if offset == "":
+ offset = 0
+
+ ticker = ticker.upper()
+ stock = helpers.load(ticker, start)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ price_df = pd.DataFrame(
+ stock["Adj Close"].values, columns=["Price"], index=stock.index
+ )
+ i = 1
+ for win in window:
+ wma_data = overlap_model.wma(
+ values=stock["Adj Close"], length=win, offset=offset
+ )
+ price_df = price_df.join(wma_data)
+ l_legend.append(f"WMA {win}")
+ i += 1
+
+ # Output Data
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=price_df.index,
+ y=price_df["Price"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ for i in range(1, price_df.shape[1]):
+ trace_name = price_df.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=price_df.index,
+ y=price_df.iloc[:, i],
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} WMA",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_wma.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/wma_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/wma_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Weighted-Moving-Average {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/bots/stocks/technical_analysis/zlma.py b/bots/stocks/technical_analysis/zlma.py
new file mode 100644
index 00000000000..98e6bd14279
--- /dev/null
+++ b/bots/stocks/technical_analysis/zlma.py
@@ -0,0 +1,138 @@
+from datetime import datetime, timedelta
+
+import pandas as pd
+import plotly.graph_objects as go
+
+import bots.config_discordbot as cfg
+from bots.config_discordbot import logger
+from bots import helpers
+from gamestonk_terminal.common.technical_analysis import overlap_model
+
+
+def zlma_command(ticker="", window="", offset="", start="", end=""):
+ """Displays chart with zero lag moving average [Yahoo Finance]"""
+
+ # Debug
+ if cfg.DEBUG:
+ logger.debug(
+ "ta-zlma %s %s %s %s %s",
+ ticker,
+ window,
+ offset,
+ start,
+ end,
+ )
+
+ # 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 end == "":
+ end = datetime.now()
+ else:
+ end = datetime.strptime(end, cfg.DATE_FORMAT)
+
+ l_legend = [ticker]
+
+ if window == "":
+ window = [20]
+ else:
+ window_temp = list()
+ for wind in window.split(","):
+ try:
+ window_temp.append(float(wind))
+ except Exception as e:
+ raise Exception("Window needs to be a float") from e
+ window = window_temp
+
+ if offset == "":
+ offset = 0
+
+ ticker = ticker.upper()
+ stock = helpers.load(ticker, start)
+ if stock.empty:
+ raise Exception("Stock ticker is invalid")
+
+ # Retrieve Data
+ price_df = pd.DataFrame(
+ stock["Adj Close"].values, columns=["Price"], index=stock.index
+ )
+ i = 1
+ for win in window:
+ zlma_data = overlap_model.zlma(
+ values=stock["Adj Close"], length=win, offset=offset
+ )
+ price_df = price_df.join(zlma_data)
+ l_legend.append(f"ZLMA {win}")
+ i += 1
+
+ # Output Data
+ start = start.strftime("%Y-%m-%d")
+ end = end.strftime("%Y-%m-%d")
+ price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
+
+ fig = go.Figure()
+ fig.add_trace(
+ go.Scatter(
+ name=f"{ticker}",
+ x=price_df.index,
+ y=price_df["Price"],
+ line=dict(color="#fdc708", width=2),
+ opacity=1,
+ ),
+ )
+ for i in range(1, price_df.shape[1]):
+ trace_name = price_df.columns[i].replace("_", " ")
+ fig.add_trace(
+ go.Scatter(
+ name=trace_name,
+ x=price_df.index,
+ y=price_df.iloc[:, i],
+ opacity=1,
+ ),
+ )
+ fig.update_layout(
+ margin=dict(l=0, r=0, t=50, b=20),
+ template=cfg.PLT_TA_STYLE_TEMPLATE,
+ colorway=cfg.PLT_TA_COLORWAY,
+ title=f"{ticker} ZLMA",
+ title_x=0.5,
+ yaxis_title="Stock Price ($)",
+ xaxis_title="Time",
+ yaxis=dict(
+ fixedrange=False,
+ ),
+ xaxis=dict(
+ rangeslider=dict(visible=False),
+ type="date",
+ ),
+ dragmode="pan",
+ legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
+ )
+ config = dict({"scrollZoom": True})
+ imagefile = "ta_zlma.png"
+
+ # Check if interactive settings are enabled
+ plt_link = ""
+ if cfg.INTERACTIVE:
+ html_ran = helpers.uuid_get()
+ fig.write_html(f"in/zlma_{html_ran}.html", config=config)
+ plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/zlma_{html_ran}.html)"
+
+ fig.update_layout(
+ width=800,
+ height=500,
+ )
+
+ imagefile = helpers.image_border(imagefile, fig=fig)
+
+ return {
+ "title": f"Stocks: Zero-Lag-Moving-Average {ticker}",
+ "description": plt_link,
+ "imagefile": imagefile,
+ }
diff --git a/discordbot/cmds/dps_cmds.py b/discordbot/cmds/dps_cmds.py
deleted file mode 100644
index c527b0e0efb..00000000000
--- a/discordbot/cmds/dps_cmds.py
+++ /dev/null
@@ -1,206 +0,0 @@
-from __future__ import annotations
-
-import disnake
-import pandas as pd
-from disnake.ext import commands
-
-from discordbot.config_discordbot import logger
-from discordbot.stocks.dark_pool_shorts.dpotc import dpotc_command
-from discordbot.stocks.dark_pool_shorts.ftd import ftd_command
-from discordbot.stocks.dark_pool_shorts.hsi import hsi_command
-from discordbot.stocks.dark_pool_shorts.pos import pos_command
-from discordbot.stocks.dark_pool_shorts.psi import psi_command
-from discordbot.stocks.dark_pool_shorts.shorted import shorted_command
-from discordbot.stocks.dark_pool_shorts.sidtc import sidtc_command
-from discordbot.stocks.dark_pool_shorts.spos import spos_command
-
-pos_choices = [
- "Short Vol (1M)",
- "Short Vol %",
- "Net Short Vol (1M)",
- "Net Short Vol ($100M)",
- "DP Position (1M)",
- "DP Position ($1B)",
-]
-
-
-def default_completion(inter: disnake.AppCmdInter) -> list[str]:
- return ["Start Typing", "for a", "stock ticker"]
-
-
-def ticker_autocomp(inter: disnake.AppCmdInter, ticker: str):
- if not ticker:
- return default_completion(inter)
- print(f"ticker_autocomp [ticker]: {ticker}")
- tlow = ticker.lower()
- col_list = ["Name"]
- df = pd.read_csv("files/tickers.csv", usecols=col_list)
- df = df["Name"]
- return [ticker for ticker in df if ticker.lower().startswith(tlow)][:24]
-
-
-class DarkPoolShortsCommands(commands.Cog):
- """Dark Pool Shorts menu"""
-
- def __init__(self, bot: commands.Bot):
- self.bot = bot
- self.bot.help_command.cog = self
-
- def cog_unload(self):
- self.bot.help_command = None
-
- @commands.slash_command(name="dps-shorted")
- async def shorted(self, ctx: disnake.AppCmdInter, num: int = 10):
- """Show most shorted stocks [Yahoo Finance]
-
- Parameters
- -----------
- num: Number of the most shorted stocks to retrieve
- """
- await ctx.response.defer()
- logger.info("dps-shorted")
- await shorted_command(ctx, num)
-
- @commands.slash_command(name="dps-hsi")
- async def hsi(self, ctx: disnake.AppCmdInter, num: int = 10):
- """Show top high short interest stocks of over 20% ratio [shortinterest.com]
-
- Parameters
- -----------
- num: Number of top stocks to print
- """
- await ctx.response.defer()
- logger.info("dps-hsi")
- await hsi_command(ctx, num)
-
- @commands.slash_command(name="dps-pos")
- async def pos(
- self,
- ctx: disnake.AppCmdInter,
- sort: str = commands.Param(choices=pos_choices),
- num: int = 10,
- ):
- """Dark pool short position [Stockgrid]
-
- Parameters
- -----------
- sort: Field for which to sort.
- num: Number of top tickers to show
- """
- await ctx.response.defer()
- logger.info("dps-pos")
-
- if str(sort) == "Short Vol (1M)":
- sort = "sv"
- if str(sort) == "Short Vol %":
- sort = "sv_pct"
- if str(sort) == "Net Short Vol (1M)":
- sort = "nsv"
- if str(sort) == "Net Short Vol ($100M)":
- sort = "nsv_dollar"
- if str(sort) == "DP Position (1M)":
- sort = "dpp"
- if str(sort) == "DP Position ($1B)":
- sort = "dpp_dollar"
-
- await pos_command(ctx, sort, num)
-
- @commands.slash_command(name="dps-sidtc")
- async def sidtc(
- self,
- ctx: disnake.AppCmdInter,
- sort: str = commands.Param(
- choices=["Float Short %", "Days to Cover", "Short Interest"]
- ),
- num: int = 10,
- ):
- """Short interest and days to cover [Stockgrid]
-
- Parameters
- -----------
- sort: Field for which to sort. Possible are: `float`, `dtc`, `si`.
- num: Number of top tickers to show
- """
- await ctx.response.defer()
- logger.info("dps-sidtc")
-
- if str(sort) == "Float Short %":
- sort = "float"
- if str(sort) == "Days to Cover":
- sort = "dtc"
- if str(sort) == "Short Interest":
- sort = "si"
-
- await sidtc_command(ctx, sort, num)
-
- @commands.slash_command(name="dps-ftd")
- async def ftd(
- self,
- ctx: disnake.AppCmdInter,
- ticker: str = commands.Param(autocomplete=ticker_autocomp),
- start="",
- end="",
- ):
- """Fails-to-deliver data [SEC]
-
- Parameters
- ----------
- ticker: Stock Ticker
- start: YYYY-MM-DD format
- end: YYYY-MM-DD format
- """
- await ctx.response.defer()
- logger.info("dps-ftd")
- await ftd_command(ctx, ticker, start, end)
-
- @commands.slash_command(name="dps-dpotc")
- async def dpotc(
- self,
- ctx: disnake.AppCmdInter,
- ticker: str = commands.Param(autocomplete=ticker_autocomp),
- ):
- """Dark pools (ATS) vs OTC data [FINRA]
-
- Parameters
- ----------
- ticker: Stock Ticker
- """
- await ctx.response.defer()
- logger.info("dps-dpotc")
- await dpotc_command(ctx, ticker)
-
- @commands.slash_command(name="dps-spos")
- async def spos(
- self,
- ctx: disnake.AppCmdInter,
- ticker: str = commands.Param(autocomplete=ticker_autocomp),
- ):
- """Net short vs position [Stockgrid]
-
- Parameters
- ----------
- ticker: Stock Ticker
- """
- await ctx.response.defer()
- logger.info("dps-spos")
- await spos_command(ctx, ticker)
-
- @commands.slash_command(name="dps-psi")
- async def psi(
- self,
- ctx: disnake.AppCmdInter,
- ticker: str = commands.Param(autocomplete=ticker_autocomp),
- ):
- """Price vs short interest volume [Stockgrid]
-
- Parameters
- ----------
- ticker: Stock Ticker
- """
- await ctx.response.defer()
- logger.info("dps-psi")
- await psi_command(ctx, ticker)
-
-
-def setup(bot: commands.Bot):
- bot.add_cog(DarkPoolShortsCommands(bot))
diff --git a/discordbot/cmds/economy_cmds.py b/discordbot/cmds/economy_cmds.py
deleted file mode 100644
index d44cd8a4c54..00000000000
--- a/discordbot/cmds/economy_cmds.py
+++ /dev/null
@@ -1,195 +0,0 @@
-from __future__ import annotations
-
-import disnake
-from disnake.ext import commands
-
-from discordbot.config_discordbot import logger
-from discordbot.economy.currencies import currencies_command
-from discordbot.economy.energy import energy_command
-from discordbot.economy.feargreed import feargreed_command
-from discordbot.economy.futures import futures_command
-from discordbot.economy.glbonds import glbonds_command
-from discordbot.economy.grains import grains_command
-from discordbot.economy.indices import indices_command
-from discordbot.economy.meats import meats_command
-from discordbot.economy.metals import metals_command
-from discordbot.economy.overview import overview_command
-from discordbot.economy.performance import performance_command
-from discordbot.economy.softs import softs_command
-from discordbot.economy.usbonds import usbonds_command
-from discordbot.economy.valuation import valuation_command
-
-group = [
- "basic_materials",
- "capitalization",
- "communication_services",
- "consumer_cyclical",
- "consumer_defensive",
- "country",
- "energy",
- "financial",
- "healthcare",
- "industry",
- "industrials",
- "real_estate",
- "sector",
- "technology",
- "utilities",
-]
-fgind = [
- "Junk Bond Demand",
- "Market Volatility",
- "Put and Call Options",
- "Market Momentum",
- "Stock Price Strength",
- "Stock Price Breadth",
- "Safe Heaven Demand",
-]
-
-
-class EconomyCommands(commands.Cog):
- """Economy Commands menu"""
-
- def __init__(self, bot: commands.Bot):
- self.bot = bot
-
- @commands.slash_command(name="econ-feargreed")
- async def feargreed(
- self, ctx: disnake.AppCmdInter, indicator: str = commands.Param(choices=fgind)
- ):
- """CNN Fear and Greed Index [CNN]
-
- Parameters
- ----------
- indicator: Select an Indicator
- """
- await ctx.response.defer()
- logger.info("econ-feargreed")
- if indicator == "Junk Bond Demand":
- indicator = "jbd"
- if indicator == "Market Volatility":
- indicator = "mv"
- if indicator == "Put and Call Options":
- indicator = "pco"
- if indicator == "Market Momentum":
- indicator = "mm"
- if indicator == "Stock Price Strength":
- indicator = "sps"
- if indicator == "Stock Price Breadth":
- indicator = "spb"
- if indicator == "Safe Heaven Demand":
- indicator = "shd"
- await feargreed_command(ctx, indicator)
-
- @commands.slash_command(name="econ-overview")
- async def overview(self, ctx: disnake.AppCmdInter):
- """Market data overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-overview")
- await overview_command(ctx)
-
- @commands.slash_command(name="econ-indices")
- async def indices(self, ctx: disnake.AppCmdInter):
- """US indices overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-indices")
- await indices_command(ctx)
-
- @commands.slash_command(name="econ-futures")
- async def futures(self, ctx: disnake.AppCmdInter):
- """Futures and commodities overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-futures")
- await futures_command(ctx)
-
- @commands.slash_command(name="econ-usbonds")
- async def usbonds(self, ctx: disnake.AppCmdInter):
- """US bonds overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-usbonds")
- await usbonds_command(ctx)
-
- @commands.slash_command(name="econ-glbonds")
- async def glbonds(self, ctx: disnake.AppCmdInter):
- """Global bonds overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-glbonds")
- await glbonds_command(ctx)
-
- @commands.slash_command(name="econ-energy")
- async def energy(self, ctx: disnake.AppCmdInter):
- """Displays energy futures data [Finviz]"""
- await ctx.response.defer()
- logger.info("econ-energy")
- await energy_command(ctx)
-
- @commands.slash_command(name="econ-metals")
- async def metals(self, ctx: disnake.AppCmdInter):
- """Displays metals futures data [Finviz]"""
- await ctx.response.defer()
- logger.info("econ-metals")
- await metals_command(ctx)
-
- @commands.slash_command(name="econ-meats")
- async def meats(self, ctx: disnake.AppCmdInter):
- """Displays meats futures data [Finviz]"""
- await ctx.response.defer()
- logger.info("econ-meats")
- await meats_command(ctx)
-
- @commands.slash_command(name="econ-grains")
- async def grains(self, ctx: disnake.AppCmdInter):
- """Displays grains futures data [Finviz]"""
- await ctx.response.defer()
- logger.info("econ-grains")
- await grains_command(ctx)
-
- @commands.slash_command(name="econ-softs")
- async def softs(self, ctx: disnake.AppCmdInter):
- """Displays softs futures data [Finviz]"""
- await ctx.response.defer()
- logger.info("econ-softs")
- await softs_command(ctx)
-
- @commands.slash_command(name="econ-currencies")
- async def currencies(self, ctx: disnake.AppCmdInter):
- """Currencies overview [Wall St. Journal]"""
- await ctx.response.defer()
- logger.info("econ-currencies")
- await currencies_command(ctx)
-
- @commands.slash_command(name="econ-valuation")
- async def valuation(
- self,
- ctx: disnake.AppCmdInter,
- economy_group: str = commands.Param(choices=group),
- ):
- """Valuation of sectors, industry, country [Finviz]
-
- Parameters
- -----------
- economy_group: Economy Group
- """
- await ctx.response.defer()
- logger.info("econ-valuation")
- await valuation_command(ctx, economy_group)
-
- @commands.slash_command(name="econ-performance")
- async def performance(
- self,
- ctx: disnake.AppCmdInter,
- economy_group: str = commands.Param(choices=group),
- ):
- """Performance of sectors, industry, country [Finviz]
-
- Parameters
- -----------
- economy_group: Economy Group
- """
- await ctx.response.defer()
- logger.info("econ-performance")
- await performance_command(ctx, economy_group)
-
-
-def setup(bot: commands.Bot):
- bot.add_cog(EconomyCommands(bot))
diff --git a/discordbot/economy/currencies.py b/discordbot/economy/currencies.py
deleted file mode 100644
index 6a177f73dcb..00000000000
--- a/discordbot/economy/currencies.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def currencies_command(ctx):
- """Currencies overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-currencies")
-
- # Retrieve data
- df = wsj_model.global_currencies()
- df = pd.DataFrame.from_dict(df)
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Last"] = pd.to_numeric(df["Last"].astype(float))
- df["Chng"] = pd.to_numeric(df["Chng"].astype(float))
- df["%Chng"] = pd.to_numeric(df["%Chng"].astype(float))
-
- formats = {"Last": "{:.2f}", "Chng": "{:.2f}", "%Chng": "{:.2f}%"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.fillna("")
- df.set_index(" ", inplace=True)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- df = df[
- [
- "Last",
- "Chng",
- "%Chng",
- ]
- ]
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
-
- imagefile = "econ-currencies.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Economy: [WSJ] Currencies"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] Currencies",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/energy.py b/discordbot/economy/energy.py
deleted file mode 100644
index 09c3aeacf03..00000000000
--- a/discordbot/economy/energy.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def energy_command(ctx):
- """Displays energy futures data [Finviz]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-energy")
-
- # Retrieve data
- d_futures = finviz_model.get_futures()
- df = pd.DataFrame(d_futures["Energy"])
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.sort_values(by="ticker", ascending=False)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- df = df.fillna("")
- df.set_index("label", inplace=True)
-
- df = df[
- [
- "prevClose",
- "last",
- "change",
- ]
- ]
- df.index.names = [""]
- df = df.rename(
- columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
- )
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 2],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-energy.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [Finviz] Energy Futures"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Energy Futures",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/feargreed.py b/discordbot/economy/feargreed.py
deleted file mode 100644
index 2cec7090f94..00000000000
--- a/discordbot/economy/feargreed.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import datetime
-import os
-
-import disnake
-import matplotlib.pyplot as plt
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from gamestonk_terminal.economy import cnn_model, cnn_view
-
-
-async def feargreed_command(ctx, indicator=""):
- """CNN Fear and Greed Index [CNN]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-futures")
-
- # Check for argument
- possible_indicators = ("", "jbd", "mv", "pco", "mm", "sps", "spb", "shd")
-
- if indicator not in possible_indicators:
- raise Exception(
- f"Select a valid indicator from {', '.join(possible_indicators)}" # nosec
- )
-
- # Retrieve data
- fig = plt.figure(figsize=[1, 1], dpi=10)
-
- report, _ = cnn_model.get_feargreed_report(indicator, fig)
- cnn_view.fear_and_greed_index(indicator=indicator, export="png")
- plt.close("all")
-
- # Output data
- now = datetime.datetime.now()
- image_path = os.path.join(
- cfg.GST_PATH,
- "exports",
- "economy",
- f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png",
- )
-
- i = 0
- while not os.path.exists(image_path) and i < 10:
- now -= datetime.timedelta(seconds=1)
- image_path = os.path.join(
- cfg.GST_PATH,
- "exports",
- "economy",
- f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png",
- )
- i += 1
-
- embed = disnake.Embed(
- title="Economy: [CNN] Fear Geed Index",
- description=report,
- colour=cfg.COLOR,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- if os.path.exists(image_path):
- uploaded_image = gst_imgur.upload_image(
- image_path, title="FearGreed Charts"
- )
- embed.set_image(url=uploaded_image.link)
-
- else:
- logger.error("Error when uploading the the image to Imgur.")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- logger.error("ERROR Economy: [CNN] Feargreed. %s", e)
- embed = disnake.Embed(
- title="ERROR Economy: [CNN] Feargreed",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/futures.py b/discordbot/economy/futures.py
deleted file mode 100644
index 4539ef0936a..00000000000
--- a/discordbot/economy/futures.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def futures_command(ctx):
- """Futures and commodities overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-futures")
-
- # Retrieve data
- df = wsj_model.top_commodities()
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Price"] = pd.to_numeric(df["Price"].astype(float))
- df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
- df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.fillna("")
- df.set_index(" ", inplace=True)
-
- df = df[
- [
- "Price",
- "Chg",
- "%Chg",
- ]
- ]
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-futures.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [WSJ] Futures/Commodities"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] Futures/Commodities",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/glbonds.py b/discordbot/economy/glbonds.py
deleted file mode 100644
index c6bf2ba36b0..00000000000
--- a/discordbot/economy/glbonds.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def glbonds_command(ctx):
- """Global bonds overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-glbonds")
-
- # Retrieve data
- df = wsj_model.global_bonds()
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Rate (%)"] = pd.to_numeric(df["Rate (%)"].astype(float))
- df["Yld (%)"] = pd.to_numeric(df["Yld (%)"].astype(float))
- df["Yld Chg (%)"] = pd.to_numeric(df["Yld Chg (%)"].astype(float))
-
- formats = {
- "Rate (%)": "{:.2f}%",
- "Yld (%)": "{:.2f}%",
- "Yld Chg (%)": "{:.2f}%",
- }
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- df = df.fillna("")
- df = df.replace(
- ("Government Bond", "10 Year"), ("Gov .Bond", "10yr"), regex=True
- )
- df.set_index(" ", inplace=True)
- df = df.set_axis(
- [
- "Rate",
- "Yld",
- "Yld Chg",
- ],
- axis="columns",
- )
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-glbonds.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [WSJ] Global Bonds"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] Global Bonds",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/grains.py b/discordbot/economy/grains.py
deleted file mode 100644
index 1702bb2b1c6..00000000000
--- a/discordbot/economy/grains.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def grains_command(ctx):
- """Displays grains futures data [Finviz]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-grains")
-
- # Retrieve data
- d_futures = finviz_model.get_futures()
- df = pd.DataFrame(d_futures["Grains"])
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.fillna("")
- df = df.set_index("label")
- df = df.sort_values(by="ticker", ascending=False)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df)
-
- df = df[
- [
- "prevClose",
- "last",
- "change",
- ]
- ]
- df.index.names = [""]
- df = df.rename(
- columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
- )
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[6, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-grains.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [Finviz] Grains Futures"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Grains Futures",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/indices.py b/discordbot/economy/indices.py
deleted file mode 100644
index 8f3dbe54a12..00000000000
--- a/discordbot/economy/indices.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def indices_command(ctx):
- """US indices overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-indices")
-
- # Retrieve data
- df = wsj_model.us_indices()
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Price"] = pd.to_numeric(df["Price"].astype(float))
- df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
- df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
-
- formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df)
-
- df = df[
- [
- "Price",
- "Chg",
- "%Chg",
- ]
- ]
-
- df = df.fillna("")
- df.set_index(" ", inplace=True)
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align=["left", "center"],
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-indices.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [WSJ] US Indices"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] US Indices",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/meats.py b/discordbot/economy/meats.py
deleted file mode 100644
index 171d059171d..00000000000
--- a/discordbot/economy/meats.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def meats_command(ctx):
- """Displays meats futures data [Finviz]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-meats")
-
- # Retrieve data
- d_futures = finviz_model.get_futures()
- df = pd.DataFrame(d_futures["Meats"])
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df = df.fillna("")
- df = df.set_index("label")
- df = df.sort_values(by="ticker", ascending=False)
-
- formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df)
-
- df = df[
- [
- "prevClose",
- "last",
- "change",
- ]
- ]
- df.index.names = [""]
- df = df.rename(
- columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
- )
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[6, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-meats.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Economy: [Finviz] Meats Futures"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Meats Futures",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/metals.py b/discordbot/economy/metals.py
deleted file mode 100644
index b11ad4abc51..00000000000
--- a/discordbot/economy/metals.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def metals_command(ctx):
- """Displays metals futures data [Finviz]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-metals")
-
- # Retrieve data
- d_futures = finviz_model.get_futures()
- df = pd.DataFrame(d_futures["Metals"])
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df)
-
- df = df.sort_values(by="ticker", ascending=False)
- df = df.fillna("")
- df.set_index("label", inplace=True)
-
- df = df[
- [
- "prevClose",
- "last",
- "change",
- ]
- ]
-
- df.index.names = [""]
- df = df.rename(
- columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
- )
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[6, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-metals.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Economy: [Finviz] Metals Futures"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Metals Futures",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/overview.py b/discordbot/economy/overview.py
deleted file mode 100644
index 3fbeba9d780..00000000000
--- a/discordbot/economy/overview.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def overview_command(ctx):
- """Market data overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-overview")
-
- # Retrieve data
- df = wsj_model.market_overview()
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Price"] = pd.to_numeric(df["Price"].astype(float))
- df["Chg"] = pd.to_numeric(df["Chg"].astype(float))
- df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float))
-
- formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"}
- for col, v in formats.items():
- df[col] = df[col].map(lambda x: v.format(x)) # pylint: disable=W0640
-
- df = df[
- [
- "Price",
- "Chg",
- "%Chg",
- ]
- ]
-
- df = df.fillna("")
- df.set_index(" ", inplace=True)
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-indices.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Economy: [WSJ] Market Overview"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] Market Overview",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/performance.py b/discordbot/economy/performance.py
deleted file mode 100644
index 4c7a15bb374..00000000000
--- a/discordbot/economy/performance.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import os
-
-import df2img
-import disnake
-import numpy as np
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def performance_command(ctx, economy_group="sector"):
- """Performance of sectors, industry, country [Finviz]"""
-
- d_economy_group = {
- "sector": "Sector",
- "industry": "Industry",
- "basic_materials": "Industry (Basic Materials)",
- "communication_services": "Industry (Communication Services)",
- "consumer_cyclical": "Industry (Consumer Cyclical)",
- "consumer_defensive": "Industry (Consumer Defensive)",
- "energy": "Industry (Energy)",
- "financial": "Industry (Financial)",
- "healthcare": "Industry (Healthcare)",
- "industrials": "Industry (Industrials)",
- "real_estate": "Industry (Real Estate)",
- "technology": "Industry (Technology)",
- "utilities": "Industry (Utilities)",
- "country": "Country (U.S. listed stocks only)",
- "capitalization": "Capitalization",
- }
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-performance %s", economy_group)
-
- # Select default group
- if not economy_group:
- if cfg.DEBUG:
- logger.debug("Use default economy_group: 'sector'")
- economy_group = "sector"
-
- # Check for argument
- possible_groups = list(d_economy_group.keys())
-
- if economy_group not in possible_groups:
- possible_group_list = ", ".join(possible_groups)
- raise Exception(f"Select a valid group from {possible_group_list}") # nosec
-
- group = d_economy_group[economy_group]
-
- # Retrieve data
- df_group = finviz_model.get_valuation_performance_data(group, "performance")
-
- # Check for argument
- if df_group.empty:
- raise Exception("No available data found")
-
- # Output data
- df = pd.DataFrame(df_group)
- df = df.replace(np.nan, 0)
-
- df["Volume"] = df["Volume"] / 1_000_000
- df["Avg Volume"] = df["Avg Volume"] / 1_000_000
-
- formats = {
- "Perf Month": "{:.2f}",
- "Perf Quart": "{:.2f}",
- "Perf Half": "{:.2f}",
- "Perf Year": "{:.2f}",
- "Perf YTD": "{:.2f}",
- "Avg Volume": "{:.0f}M",
- "Change": "{:.2f}",
- "Volume": "{:.0f}M",
- }
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.set_axis(
- [
- "Name",
- "Week",
- "Month",
- "3Month",
- "6Month",
- "1Year",
- "YTD",
- "Recom",
- "Avg Vol.",
- "RelVolume",
- "Change",
- "Volume",
- ],
- axis="columns",
- )
-
- df = df.fillna("")
- df.set_index("Name", inplace=True)
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(1500, (40 + (50 * dindex))),
- col_width=[10, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3.5],
- tbl_cells=dict(
- align=["left", "center"],
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-performance.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = f"Economy: [Finviz] Performance {group}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Performance",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/softs.py b/discordbot/economy/softs.py
deleted file mode 100644
index 491396131b5..00000000000
--- a/discordbot/economy/softs.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def softs_command(ctx):
- """Displays softs futures data [Finviz]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-softs")
-
- # Retrieve data
- d_futures = finviz_model.get_futures()
- df = pd.DataFrame(d_futures["Softs"])
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- formats = {"last": "${:.2f}", "prevClose": "${:.2f}"}
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df)
-
- df = df.sort_values(by="ticker", ascending=False)
- df = df.fillna("")
- df.set_index("label", inplace=True)
-
- df = df[
- [
- "prevClose",
- "last",
- "change",
- ]
- ]
-
- df.index.names = [""]
- df = df.rename(
- columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"}
- )
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[6, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-softs.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Economy: [Finviz] Softs Futures"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Softs Futures",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/usbonds.py b/discordbot/economy/usbonds.py
deleted file mode 100644
index c4652530390..00000000000
--- a/discordbot/economy/usbonds.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import wsj_model
-
-
-async def usbonds_command(ctx):
- """US bonds overview [Wall St. Journal]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-usbonds")
-
- # Retrieve data
- df = wsj_model.us_bonds()
-
- # Check for argument
- if df.empty:
- raise Exception("No available data found")
-
- df["Rate (%)"] = pd.to_numeric(df["Rate (%)"].astype(float))
- df["Yld (%)"] = pd.to_numeric(df["Yld (%)"].astype(float))
- df["Yld Chg (%)"] = pd.to_numeric(df["Yld Chg (%)"].astype(float))
-
- formats = {
- "Rate (%)": "{:.2f}%",
- "Yld (%)": "{:.2f}%",
- "Yld Chg (%)": "{:.2f}%",
- }
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.fillna("")
- df.set_index(" ", inplace=True)
-
- df = df.set_axis(
- [
- "Rate",
- "Yld",
- "Yld Chg",
- ],
- axis="columns",
- )
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[8, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-usbonds.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Economy: [WSJ] US Bonds"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [WSJ] US Bonds",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/economy/valuation.py b/discordbot/economy/valuation.py
deleted file mode 100644
index 6b69b8cc660..00000000000
--- a/discordbot/economy/valuation.py
+++ /dev/null
@@ -1,158 +0,0 @@
-import os
-
-import df2img
-import disnake
-import numpy as np
-import pandas as pd
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.economy import finviz_model
-
-
-async def valuation_command(ctx, economy_group="sector"):
- """Valuation of sectors, industry, country [Finviz]"""
-
- d_economy_group = {
- "sector": "Sector",
- "industry": "Industry",
- "basic_materials": "Industry (Basic Materials)",
- "communication_services": "Industry (Communication Services)",
- "consumer_cyclical": "Industry (Consumer Cyclical)",
- "consumer_defensive": "Industry (Consumer Defensive)",
- "energy": "Industry (Energy)",
- "financial": "Industry (Financial)",
- "healthcare": "Industry (Healthcare)",
- "industrials": "Industry (Industrials)",
- "real_estate": "Industry (Real Estate)",
- "technology": "Industry (Technology)",
- "utilities": "Industry (Utilities)",
- "country": "Country (U.S. listed stocks only)",
- "capitalization": "Capitalization",
- }
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("econ-valuation %s", economy_group)
-
- # Select default group
- if economy_group == "":
- if cfg.DEBUG:
- logger.debug("Use default economy_group: 'sector'")
- economy_group = "sector"
-
- # Check for argument
- possible_groups = list(d_economy_group.keys())
-
- if economy_group not in possible_groups:
- raise Exception(
- f"Select a valid group from {', '.join(possible_groups)}" # nosec
- )
-
- group = d_economy_group[economy_group]
-
- # Retrieve data
- df_group = finviz_model.get_valuation_performance_data(group, "valuation")
-
- # Check for argument
- if df_group.empty:
- raise Exception("No available data found")
-
- # Output data
- df = pd.DataFrame(df_group)
- df = df.replace(np.nan, 0)
-
- df = df.set_axis(
- [
- "Name",
- "MarketCap",
- "P/E",
- "FwdP/E",
- "PEG",
- "P/S",
- "P/B",
- "P/C",
- "P/FCF",
- "EPSpast5Y",
- "EPSnext5Y",
- "Salespast5Y",
- "Change",
- "Volume",
- ],
- axis="columns",
- )
-
- df["P/E"] = pd.to_numeric(df["P/E"].astype(float))
- df["FwdP/E"] = pd.to_numeric(df["FwdP/E"].astype(float))
- df["EPSpast5Y"] = pd.to_numeric(df["EPSpast5Y"].astype(float))
- df["EPSnext5Y"] = pd.to_numeric(df["EPSnext5Y"].astype(float))
- df["Salespast5Y"] = pd.to_numeric(df["Salespast5Y"].astype(float))
- df["Volume"] = pd.to_numeric(df["Volume"].astype(float))
- df["Volume"] = df["Volume"] / 1_000_000
-
- formats = {
- "P/E": "{:.2f}",
- "FwdP/E": "{:.2f}",
- "EPSpast5Y": "{:.2f}",
- "EPSnext5Y": "{:.2f}",
- "Salespast5Y": "{:.2f}",
- "Change": "{:.2f}",
- "Volume": "{:.0f}M",
- }
- for col, value in formats.items():
- df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640
-
- df = df.fillna("")
- df.set_index("Name", inplace=True)
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(1600, (40 + (50 * dindex))),
- col_width=[12, 5, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 4, 4],
- tbl_cells=dict(
- align=["left", "center"],
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "econ-valuation.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = f"Economy: [Finviz] Valuation {group}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Economy: [Finviz] Valuation",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/helpers.py b/discordbot/helpers.py
deleted file mode 100644
index 1c5f892cea9..00000000000
--- a/discordbot/helpers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from PIL import Image
-import yfinance as yf
-
-
-def load(ticker, start_date):
- df_stock_candidate = yf.download(ticker, start=start_date, progress=False)
- df_stock_candidate.index.name = "date"
- return df_stock_candidate
-
-
-def autocrop_image(image, border=0):
- bbox = image.getbbox()
- image = image.crop(bbox)
- (width, height) = image.size
- width += border * 2
- height += border * 2
- cropped_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
- cropped_image.paste(image, (border, border))
- return cropped_image
diff --git a/discordbot/reaction_helper.py b/discordbot/reaction_helper.py
deleted file mode 100644
index 6c5a4e9ad56..00000000000
--- a/discordbot/reaction_helper.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import asyncio
-import discord
-
-from gamestonk_terminal.config_terminal import TRADIER_TOKEN
-from gamestonk_terminal.stocks.options import tradier_model, yfinance_model
-
-import discordbot.config_discordbot as cfg
-from discordbot.run_discordbot import gst_bot, logger
-
-
-async def expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg: tuple = None):
- if TRADIER_TOKEN == "REPLACE_ME": # nosec
- dates = yfinance_model.option_expirations(ticker)
- else:
- dates = tradier_model.option_expirations(ticker)
-
- index_dates = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-
- if expiry is not None:
- try:
- if expiry not in dates:
- exp = int(expiry.replace("-", ""))
-
- if (expiry not in dates) and (exp not in index_dates):
- raise Exception("Enter a valid expiration date.")
-
- if expiry in dates:
- if call_arg is None:
- await func_cmd(ctx, ticker, expiry)
- else:
- await func_cmd(ctx, ticker, expiry, *call_arg)
- return
- if exp in index_dates:
- expiry = dates[int(expiry)]
- if call_arg is None:
- await func_cmd(ctx, ticker, expiry)
- else:
- await func_cmd(ctx, ticker, expiry, *call_arg)
- return
-
- except Exception as e:
- embed = discord.Embed(
- title="ERROR Options: Expiry Date",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- await ctx.send(embed=embed, delete_after=10.0)
- return
-
- if not dates:
- embed = discord.Embed(
- title="ERROR Options",
- colour=cfg.COLOR,
- description="Enter a valid stock ticker",
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=10.0)
- return
-
- text = (
- "```0️⃣ " + dates[0] + "\n"
- "1️⃣ " + dates[1] + "\n"
- "2️⃣ " + dates[2] + "\n"
- "3️⃣ " + dates[3] + "\n"
- "4️⃣ " + dates[4] + "\n"
- "5️⃣ " + dates[5] + "\n"
- "6️⃣ " + dates[6] + "\n"
- "7️⃣ " + dates[7] + "\n"
- "8️⃣ " + dates[8] + "\n"
- "9️⃣ " + dates[9] + "```"
- )
-
- title = " " + ticker.upper() + " Options: Expiry Date"
- 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, delete_after=15.0)
-
- emoji_list = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]
-
- 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
- )
- for N in range(0, 10):
- if reaction.emoji == emoji_list[N]:
- logger.info("Reaction selected: %d", N)
- expiry = dates[N]
- if call_arg is None:
- await func_cmd(ctx, ticker, expiry)
- else:
- await func_cmd(ctx, ticker, expiry, *call_arg)
-
- 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)
- if cfg.DEBUG:
- embed = discord.Embed(
- description="Error timeout - you snooze you lose! 😋",
- colour=cfg.COLOR,
- title="TIMEOUT " + ticker.upper() + " Options: Expiry Date",
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- await ctx.send(embed=embed, delete_after=10.0)
diff --git a/discordbot/stocks/candle.py b/discordbot/stocks/candle.py
deleted file mode 100644
index c8027115840..00000000000
--- a/discordbot/stocks/candle.py
+++ /dev/null
@@ -1,240 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-import yfinance as yf
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def candle_command(
- ctx,
- ticker: str = "",
- interval: int = 15,
- past_days: int = 1,
- start="",
- end="",
-):
- """Shows candle plot of loaded ticker. [Source: Yahoo Finance, IEX Cloud or Alpha Vantage]
-
- Parameters
- ----------
- ticker: str
- Ticker name
- interval: int
- chart mins or daily
- past_days: int
- Display the past * days. Default: 1(Not for Daily)
- start: str
- start date format YYYY-MM-DD
- end: str
- end date format YYYY-MM-DD
- """
- try:
-
- logger.info("candle %s %s %s %s", ticker, interval, start, end)
-
- if start == "":
- start = datetime.now() - timedelta(days=365)
- else:
- start = datetime.strptime(start, cfg.DATE_FORMAT)
- if end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- futures = "=F"
- if interval == 1440:
- df_stock_candidate = yf.download(
- ticker,
- start=start,
- end=end,
- progress=False,
- )
-
- # Check that loading a stock was not successful
- if df_stock_candidate.empty:
- raise Exception(f"No data found for {ticker.upper()}")
-
- df_stock_candidate.index.name = "date"
- else:
- s_int = str(interval) + "m"
- d_granularity = {
- "1m": past_days,
- "5m": past_days,
- "15m": past_days,
- "30m": past_days,
- "60m": past_days,
- }
- s_start_dt = datetime.utcnow() - timedelta(days=d_granularity[s_int])
- s_date_start = s_start_dt.strftime("%Y-%m-%d")
-
- df_stock_candidate = yf.download(
- ticker,
- start=s_date_start
- if s_start_dt > start
- else start.strftime("%Y-%m-%d"),
- progress=False,
- interval=s_int,
- prepost=True,
- )
-
- # Check that loading a stock was not successful
- if df_stock_candidate.empty:
- raise Exception(f"No data found for {ticker.upper()}.")
-
- df_stock_candidate.index = df_stock_candidate.index.tz_localize(None)
- df_stock_candidate.index.name = "date"
- df_stock = df_stock_candidate
- price_df = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_vwap = overlap_model.vwap(price_df, 0)
-
- plt_title = [f"{ticker.upper()} Intraday {interval}min", "Volume"]
- title = f"Intraday {interval}min Chart for {ticker.upper()}"
- if interval == 1440:
- plt_title = [f"{ticker.upper()} Daily", "Volume"]
- title = f"Daily Chart for {ticker.upper()}"
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.06,
- subplot_titles=plt_title,
- row_width=[0.2, 0.7],
- )
- fig.add_trace(
- go.Candlestick(
- x=df_stock.index,
- open=df_stock.Open,
- high=df_stock.High,
- low=df_stock.Low,
- close=df_stock.Close,
- name="OHLC",
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- colors = [
- "green" if row.Open < row["Adj Close"] else "red"
- for _, row in df_stock.iterrows()
- ]
- fig.add_trace(
- go.Bar(
- x=df_stock.index,
- y=df_stock.Volume,
- name="Volume",
- marker_color=colors,
- showlegend=False,
- ),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=25, b=20),
- template=cfg.PLT_CANDLE_STYLE_TEMPLATE,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- showspikes=True,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- showspikes=True,
- ),
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- dragmode="pan",
- hovermode="x unified",
- )
- if interval != 1440:
- fig.add_trace(
- go.Scatter(
- name="VWAP",
- x=df_stock.index,
- y=df_vwap["VWAP_D"],
- opacity=1,
- line=dict(color="#d81aea", width=2),
- showlegend=True,
- ),
- )
- if futures in ticker.upper():
- fig.update_xaxes(
- rangebreaks=[
- dict(bounds=["fri", "sun"]),
- dict(bounds=[17, 17.5], pattern="hour"),
- ],
- )
- else:
- fig.update_xaxes(
- rangebreaks=[
- dict(bounds=["sat", "mon"]),
- dict(bounds=[19, 9.5], pattern="hour"),
- ],
- )
- config = dict({"scrollZoom": True})
- rand = random.randint(69, 69420)
- imagefile = f"candle{rand}.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- fig.write_html(f"in/candle_{rand}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/candle_{rand}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Intraday",
- colour=cfg.COLOR,
- description=e,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/dpotc.py b/discordbot/stocks/dark_pool_shorts/dpotc.py
deleted file mode 100644
index 87b4f35a144..00000000000
--- a/discordbot/stocks/dark_pool_shorts/dpotc.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import os
-
-import disnake
-import matplotlib.dates as mdates
-import yfinance as yf
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.dark_pool_shorts import finra_model
-
-
-async def dpotc_command(ctx, ticker: str = ""):
- """Dark pools (ATS) vs OTC data [FINRA]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.dpotc %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- ticker = ticker.upper()
-
- stock = yf.download(ticker, progress=False)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve data
- ats, otc = finra_model.getTickerFINRAdata(ticker)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(ats.to_string())
- logger.debug(otc.to_string())
-
- # Output data
- title = f"Stocks: [FINRA] Dark Pools (ATS) vs OTC {ticker}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- plt.style.use("seaborn")
- plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- if ats.empty and otc.empty:
- raise Exception("Stock ticker is invalid")
- _, _ = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- plt.subplot(3, 1, (1, 2))
- if not ats.empty and not otc.empty:
- plt.bar(
- ats.index,
- (ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"])
- / 1_000_000,
- color="tab:orange",
- )
- plt.bar(
- otc.index,
- otc["totalWeeklyShareQuantity"] / 1_000_000,
- color="tab:blue",
- )
- plt.legend(["ATS", "OTC"])
-
- elif not ats.empty:
- plt.bar(
- ats.index,
- ats["totalWeeklyShareQuantity"] / 1_000_000,
- color="tab:orange",
- )
- plt.legend(["ATS"])
-
- elif not otc.empty:
- plt.bar(
- otc.index,
- otc["totalWeeklyShareQuantity"] / 1_000_000,
- color="tab:blue",
- )
- plt.legend(["OTC"])
-
- plt.ylabel("Total Weekly Shares [Million]")
- plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2)
- plt.title(f"Dark Pools (ATS) vs OTC (Non-ATS) Data for {ticker}")
-
- plt.subplot(313)
- if not ats.empty:
- plt.plot(
- ats.index,
- ats["totalWeeklyShareQuantity"] / ats["totalWeeklyTradeCount"],
- color="tab:orange",
- )
- plt.legend(["ATS"])
-
- if not otc.empty:
- plt.plot(
- otc.index,
- otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
- color="tab:blue",
- )
- plt.legend(["ATS", "OTC"])
-
- else:
- plt.plot(
- otc.index,
- otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
- color="tab:blue",
- )
- plt.legend(["OTC"])
-
- plt.ylabel("Shares per Trade")
- plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2)
- plt.gcf().autofmt_xdate()
- plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%m/%d"))
- plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=4))
- plt.xlabel("Weeks")
- file_name = ticker + "_dpotc.png"
- plt.savefig(file_name)
- imagefile = file_name
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- plt.close("all")
- uploaded_image = gst_imgur.upload_image(file_name, title="something")
- image_link = uploaded_image.link
- embed.set_image(url=image_link)
- os.remove(file_name)
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [FINRA] Dark Pools (ATS) vs OTC {ticker}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/ftd.py b/discordbot/stocks/dark_pool_shorts/ftd.py
deleted file mode 100644
index a10e9eecf73..00000000000
--- a/discordbot/stocks/dark_pool_shorts/ftd.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.dark_pool_shorts import sec_model
-
-
-async def ftd_command(ctx, ticker: str = "", start="", end=""):
- """Fails-to-deliver data [SEC]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.ftd %s %s %s", ticker, start, end)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- ticker = ticker.upper()
-
- if start == "":
- start = datetime.now() - timedelta(days=365)
- else:
- start = datetime.strptime(start, cfg.DATE_FORMAT)
-
- if end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- # Retrieve data
- ftds_data = sec_model.get_fails_to_deliver(ticker, start, end, 0)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(ftds_data.to_string())
-
- stock = discordbot.helpers.load(ticker, start)
- stock_ftd = stock[stock.index > start]
- stock_ftd = stock_ftd[stock_ftd.index < end]
-
- # Output data
- fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]])
-
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=stock_ftd.index,
- y=stock_ftd["Adj Close"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- secondary_y=False,
- )
- fig.add_trace(
- go.Bar(
- name="FTDs",
- x=ftds_data["SETTLEMENT DATE"],
- y=ftds_data["QUANTITY (FAILS)"] / 1000,
- opacity=1,
- ),
- secondary_y=True,
- )
- # Set y-axes titles
- fig.update_yaxes(title_text="Shares [K]", secondary_y=True)
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker}",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- side="right",
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- fixedrange=False,
- ),
- xaxis2=dict(
- rangeslider=dict(visible=False),
- type="date",
- fixedrange=False,
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- yaxis2=dict(
- side="left",
- position=0.15,
- fixedrange=False,
- ),
- hovermode="x unified",
- )
- config = dict({"scrollZoom": True})
- imagefile = "dps_ftd.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/ftds_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ftds_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: [SEC] Failure-to-deliver " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [SEC] Failure-to-deliver",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/hsi.py b/discordbot/stocks/dark_pool_shorts/hsi.py
deleted file mode 100644
index 3a137198748..00000000000
--- a/discordbot/stocks/dark_pool_shorts/hsi.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.dark_pool_shorts import shortinterest_model
-
-
-async def hsi_command(ctx, num: int = 10):
- """Show top high short interest stocks of over 20% ratio [shortinterest.com]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.hsi %s", num)
-
- # Check for argument
- if num < 0:
- raise Exception("Number has to be above 0")
-
- # Retrieve data
- df = shortinterest_model.get_high_short_interest()
- df = df.iloc[1:].head(n=num)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- future_column_name = df["Ticker"]
- df = df.transpose()
- df.columns = future_column_name
- df.drop("Ticker")
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df.columns.values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [highshortinterest.com] Top High Short Interest",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [highshortinterest.com] Top High Short Interest",
- description="```" + df[column].fillna("").to_string() + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [highshortinterest.com] Top High Short Interest",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/pos.py b/discordbot/stocks/dark_pool_shorts/pos.py
deleted file mode 100644
index 2cb7fbbde7d..00000000000
--- a/discordbot/stocks/dark_pool_shorts/pos.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
-
-
-async def pos_command(ctx, sort="dpp_dollar", num: int = 10):
- """Dark pool short position [Stockgrid]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.pos %s %s", sort, num)
-
- # Check for argument
- possible_sorts = ("sv", "sv_pct", "nsv", "nsv_dollar", "dpp", "dpp_dollar")
-
- if sort not in possible_sorts:
- raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}")
-
- if num < 0:
- raise Exception("Number has to be above 0")
-
- # Retrieve data
- df = stockgrid_model.get_dark_pool_short_positions(sort, False)
- df = df.iloc[:num]
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- dp_date = df["Date"].values[0]
- df = df.drop(columns=["Date"])
- df["Net Short Volume $"] = df["Net Short Volume $"] / 100_000_000
- df["Short Volume"] = df["Short Volume"] / 1_000_000
- df["Net Short Volume"] = df["Net Short Volume"] / 1_000_000
- df["Short Volume %"] = df["Short Volume %"] * 100
- df["Dark Pools Position $"] = df["Dark Pools Position $"] / (1_000_000_000)
- df["Dark Pools Position"] = df["Dark Pools Position"] / 1_000_000
- df.columns = [
- "Ticker",
- "Short Vol. (1M)",
- "Short Vol. %",
- "Net Short Vol. (1M)",
- "Net Short Vol. ($100M)",
- "DP Position (1M)",
- "DP Position ($1B)",
- ]
- future_column_name = df["Ticker"]
- df = df.transpose()
- df.columns = future_column_name
- df.drop("Ticker")
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df.columns.values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Stockgrid] Dark Pool Short Position",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df.columns.values:
- columns.append(
- disnake.Embed(
- title="High Short Interest",
- description="```The following data corresponds to the date: "
- + dp_date
- + "\n\n"
- + df[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Stockgrid] Dark Pool Short Position",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/psi.py b/discordbot/stocks/dark_pool_shorts/psi.py
deleted file mode 100644
index 5d5e7ac0e04..00000000000
--- a/discordbot/stocks/dark_pool_shorts/psi.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import os
-from datetime import timedelta
-
-import disnake
-import yfinance as yf
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
-
-
-async def psi_command(ctx, ticker: str = ""):
- """Price vs short interest volume [Stockgrid]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.psi %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- ticker = ticker.upper()
-
- stock = yf.download(ticker, progress=False)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve data
- df, prices = stockgrid_model.get_short_interest_volume(ticker)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- title = f"Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- plt.style.use("seaborn")
- _, axes = plt.subplots(
- 2,
- 1,
- figsize=plot_autoscale(),
- dpi=PLOT_DPI,
- gridspec_kw={"height_ratios": [2, 1]},
- )
-
- axes[0].bar(
- df["date"],
- df["total_volume"] / 1_000_000,
- width=timedelta(days=1),
- color="b",
- alpha=0.4,
- label="Total Volume",
- )
- axes[0].bar(
- df["date"],
- df["short_volume"] / 1_000_000,
- width=timedelta(days=1),
- color="r",
- alpha=0.4,
- label="Short Volume",
- )
-
- axes[0].set_ylabel("Volume (1M)")
- ax2 = axes[0].twinx()
- ax2.plot(
- df["date"].values,
- prices[len(prices) - len(df) :], # noqa: E203
- c="k",
- label="Price",
- )
- ax2.set_ylabel("Price ($)")
-
- lines, labels = axes[0].get_legend_handles_labels()
- lines2, labels2 = ax2.get_legend_handles_labels()
- ax2.legend(lines + lines2, labels + labels2, loc="upper left")
-
- axes[0].grid()
- axes[0].ticklabel_format(style="plain", axis="y")
- plt.title(f"Price vs Short Volume Interest for {ticker}")
- plt.gcf().autofmt_xdate()
-
- axes[1].plot(
- df["date"].values,
- 100 * df["short_volume%"],
- c="green",
- label="Short Vol. %",
- )
-
- axes[1].set_ylabel("Short Vol. %")
-
- axes[1].grid(axis="y")
- lines, labels = axes[1].get_legend_handles_labels()
- axes[1].legend(lines, labels, loc="upper left")
- axes[1].set_ylim([0, 100])
- file_name = ticker + "_psi.png"
- plt.savefig(file_name)
- imagefile = file_name
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- plt.close("all")
- uploaded_image = gst_imgur.upload_image(file_name, title="something")
- image_link = uploaded_image.link
- embed.set_image(url=image_link)
- os.remove(file_name)
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/shorted.py b/discordbot/stocks/dark_pool_shorts/shorted.py
deleted file mode 100644
index 2bf75fc6deb..00000000000
--- a/discordbot/stocks/dark_pool_shorts/shorted.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.dark_pool_shorts import yahoofinance_model
-
-
-async def shorted_command(ctx, num: int = 10):
- """Show most shorted stocks [Yahoo Finance]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.shorted %s", num)
-
- # Check for argument
- if num < 0:
- raise Exception("Number has to be above 0")
-
- # Retrieve data
- df = yahoofinance_model.get_most_shorted().head(num)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- df.dropna(how="all", axis=1, inplace=True)
- df = df.replace(float("NaN"), "")
- future_column_name = df["Symbol"]
- df = df.transpose()
- df.columns = future_column_name
- df.drop("Symbol")
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df.columns.values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
- )
- i += 1
-
- columns.append(
- disnake.Embed(
- title="Stocks: [Yahoo Finance] Most Shorted",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df.columns.values:
- columns.append(
- disnake.Embed(
- description="```" + df[column].fillna("").to_string() + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Yahoo Finance] Most Shorted",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/sidtc.py b/discordbot/stocks/dark_pool_shorts/sidtc.py
deleted file mode 100644
index 9654f7d7b10..00000000000
--- a/discordbot/stocks/dark_pool_shorts/sidtc.py
+++ /dev/null
@@ -1,104 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
-
-
-async def sidtc_command(ctx, sort="float", num: int = 10):
- """Short interest and days to cover [Stockgrid]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.dps.sidtc %s %s", sort, num)
-
- # Check for argument
- possible_sorts = ("float", "dtc", "si")
-
- if sort not in possible_sorts:
- raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}")
-
- if num < 0:
- raise Exception("Number has to be above 0")
-
- # Retrieve data
- df = stockgrid_model.get_short_interest_days_to_cover(sort)
- df = df.iloc[:num]
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- dp_date = df["Date"].values[0]
- df = df.drop(columns=["Date"])
- df["Short Interest"] = df["Short Interest"] / 1_000_000
- df.head()
- df.columns = [
- "Ticker",
- "Float Short %",
- "Days to Cover",
- "Short Interest (1M)",
- ]
- future_column_name = df["Ticker"]
- df = df.transpose()
- df.columns = future_column_name
- df.drop("Ticker")
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df.columns.values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Dark Pool Shorts", description=initial_str, colour=cfg.COLOR
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Stockgrid] Short Interest and Days to Cover",
- description="```The following data corresponds to the date: "
- + dp_date
- + "\n\n"
- + df[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Stockgrid] Short Interest and Days to Cover",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/dark_pool_shorts/spos.py b/discordbot/stocks/dark_pool_shorts/spos.py
deleted file mode 100644
index d7716878f02..00000000000
--- a/discordbot/stocks/dark_pool_shorts/spos.py
+++ /dev/null
@@ -1,119 +0,0 @@
-import os
-
-import disnake
-import yfinance as yf
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.dark_pool_shorts import stockgrid_model
-
-
-async def spos_command(ctx, ticker: str = ""):
- """Net short vs position [Stockgrid]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dps.spos %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- ticker = ticker.upper()
-
- stock = yf.download(ticker, progress=False)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve data
- df = stockgrid_model.get_net_short_position(ticker)
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(df.to_string())
-
- # Output data
- title = f"Stocks: [Stockgrid] Net Short vs Position {ticker}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- ax = fig.add_subplot(111)
- ax.bar(
- df["dates"],
- df["dollar_net_volume"] / 1_000,
- color="r",
- alpha=0.4,
- label="Net Short Vol. (1k $)",
- )
- ax.set_ylabel("Net Short Vol. (1k $)")
-
- ax2 = ax.twinx()
- ax2.plot(
- df["dates"].values,
- df["dollar_dp_position"],
- c="tab:blue",
- label="Position (1M $)",
- )
- ax2.set_ylabel("Position (1M $)")
-
- lines, labels = ax.get_legend_handles_labels()
- lines2, labels2 = ax2.get_legend_handles_labels()
- ax2.legend(lines + lines2, labels + labels2, loc="upper left")
-
- ax.grid()
- plt.title(f"Net Short Vol. vs Position for {ticker}")
- plt.gcf().autofmt_xdate()
- file_name = ticker + "_spos.png"
- plt.savefig(file_name)
- imagefile = file_name
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- plt.close("all")
- uploaded_image = gst_imgur.upload_image(file_name, title="something")
- image_link = uploaded_image.link
- embed.set_image(url=image_link)
- os.remove(file_name)
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [Stockgrid] Net Short vs Position {ticker}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/disc/ford.py b/discordbot/stocks/disc/ford.py
deleted file mode 100644
index a764fd08065..00000000000
--- a/discordbot/stocks/disc/ford.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import os
-
-import df2img
-import disnake
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.discovery import fidelity_model
-
-
-async def ford_command(ctx):
- """Display Orders by Fidelity Customers. [Source: Fidelity]
-
- Parameters
- ----------
- num: Number of stocks to display
- """
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug("!ford")
- order_header, df_orders = fidelity_model.get_orders()
-
- df_orders = df_orders.head(n=30).iloc[:, :-1]
- df_orders = df_orders.applymap(str)
-
- font_color = (
- ["white"] * 2
- + [
- [
- "#ad0000" if boolv else "#0d5700"
- for boolv in df_orders["Price Change"].str.contains("-")
- ]
- ]
- + [["white"] * 3]
- )
-
- df_orders.set_index("Symbol", inplace=True)
- df_orders = df_orders.apply(lambda x: x.str.slice(0, 20))
-
- dindex = len(df_orders.index)
- fig = df2img.plot_dataframe(
- df_orders,
- fig_size=(1500, (40 + (40 * dindex))),
- col_width=[1, 3, 2.2, 3, 2, 2],
- tbl_cells=dict(
- align=["left", "center", "left", "left", "center"],
- font=dict(color=font_color),
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "disc-ford.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = "Fidelity Customer Orders"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Fidelity Customer Orders",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/analyst.py b/discordbot/stocks/due_diligence/analyst.py
deleted file mode 100644
index 27131410617..00000000000
--- a/discordbot/stocks/due_diligence/analyst.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import os
-
-import df2img
-import disnake
-import numpy as np
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.due_diligence import finviz_model
-
-
-async def analyst_command(ctx, ticker=""):
- """Displays analyst recommendations [Finviz]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.dd.analyst %s", ticker)
-
- # Check for argument
- if not ticker:
- raise Exception("Stock ticker is required")
-
- df = finviz_model.get_analyst_data(ticker)
- df = df.replace(np.nan, 0)
- df.index.names = ["Date"]
- df = df.rename(
- columns={
- "category": "Category",
- "analyst": "Analyst",
- "rating": "Rating",
- "target": "Target",
- "target_from": "Target From",
- "target_to": "Target To",
- }
- )
-
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(1500, (40 + (40 * dindex))),
- col_width=[5, 5, 8, 14, 5, 5, 5],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "dd-analyst.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- title = f"Stocks: [Finviz] Analyst Recommendations {ticker.upper()}"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/arktrades.py b/discordbot/stocks/due_diligence/arktrades.py
deleted file mode 100644
index b4de85c7ec7..00000000000
--- a/discordbot/stocks/due_diligence/arktrades.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from menus.menu import Menu
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.due_diligence import ark_model
-
-
-async def arktrades_command(ctx, ticker: str = "", num: int = 10):
- """Displays trades made by ark [cathiesark.com]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dd.arktrades %s", ticker)
-
- if ticker:
- ark_holdings = ark_model.get_ark_trades_by_ticker(ticker)
- ark_holdings = ark_holdings.drop(
- columns=["ticker", "everything.profile.companyName"]
- )
-
- if ark_holdings.empty:
- raise Exception(
- "Issue getting data from cathiesark.com. Likely no trades found.\n"
- )
-
- ark_holdings["Total"] = ark_holdings["Total"] / 1_000_000
- ark_holdings.rename(columns={"direction": "B/S", "weight": "F %"}, inplace=True)
-
- ark_holdings.index = pd.Series(ark_holdings.index).apply(
- lambda x: x.strftime("%Y-%m-%d")
- )
-
- df = ark_holdings.head(num)
- dindex = len(df.head(num).index)
- formats = {"Close": "{:.2f}", "Total": "{:.2f}"}
- for col, f in formats.items():
- df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640
-
- title = f"Stocks: [cathiesark.com] {ticker.upper()} Trades by Ark"
-
- embeds: list = []
-
- i, i2, end = 0, 0, 20
- df_pg = []
- embeds_img = []
- dindex = len(df.index)
- while i < dindex:
- df_pg = df.iloc[i:end]
- df_pg.append(df_pg)
- fig = df2img.plot_dataframe(
- df_pg,
- fig_size=(900, (40 + (40 * 20))),
- col_width=[5, 10, 4, 4, 3, 4, 5],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = f"disc-insider{i}.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- image_link = uploaded_image.link
- embeds_img.append(
- f"{image_link}",
- )
- embeds.append(
- disnake.Embed(
- title=title,
- colour=cfg.COLOR,
- ),
- )
- i2 += 1
- i += 20
- end += 20
- os.remove(imagefile)
-
- # Author/Footer
- for i in range(0, i2):
- embeds[i].set_author(
- name=cfg.AUTHOR_NAME,
- url=cfg.AUTHOR_URL,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embeds[i].set_footer(
- text=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- i = 0
- for i in range(0, i2):
- embeds[i].set_image(url=embeds_img[i])
-
- i += 1
- embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
- options = [
- disnake.SelectOption(label="Home", value="0", emoji="🟢"),
- ]
-
- await ctx.send(embed=embeds[0], view=Menu(embeds, options))
-
- except Exception as e:
- embed = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/customer.py b/discordbot/stocks/due_diligence/customer.py
deleted file mode 100644
index 237f5e63c42..00000000000
--- a/discordbot/stocks/due_diligence/customer.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.due_diligence import csimarket_model
-
-
-async def customer_command(ctx, ticker: str = ""):
- """Displays customers of the company [CSIMarket]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.dd.customer %s", ticker)
-
- if not ticker:
- raise Exception("A ticker is required")
-
- tickers = csimarket_model.get_customers(ticker)
-
- if not tickers:
- raise Exception("Enter a valid ticker")
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(tickers)
-
- # Output data
- embed = disnake.Embed(
- title=f"Stocks: [CSIMarket] {ticker} Customers",
- description=f"{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 = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/est.py b/discordbot/stocks/due_diligence/est.py
deleted file mode 100644
index 5026ed2b208..00000000000
--- a/discordbot/stocks/due_diligence/est.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import os
-
-import df2img
-import disnake
-from menus.menu import Menu
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.due_diligence import business_insider_model
-
-
-async def est_command(ctx, ticker: str = ""):
- """Displays earning estimates [Business Insider]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.dd.est %s", 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:
- logger.debug(df_year_estimates.to_string())
- logger.debug(df_quarter_earnings.to_string())
- logger.debug(df_quarter_revenues.to_string())
-
- dindex = len(df_year_estimates.index)
- fig = df2img.plot_dataframe(
- df_year_estimates,
- fig_size=(1200, (40 + (60 * dindex))),
- col_width=[12, 4, 4, 4, 4],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "estimates.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- link_estimates = uploaded_image.link
-
- os.remove(imagefile)
-
- fig = df2img.plot_dataframe(
- df_quarter_earnings,
- fig_size=(1200, (40 + (40 * 20))),
- col_width=[5, 5, 4, 4, 5, 4],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "earnings.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- link_earnings = uploaded_image.link
- os.remove(imagefile)
-
- fig = df2img.plot_dataframe(
- df_quarter_revenues,
- fig_size=(1200, (40 + (40 * 20))),
- col_width=[5, 5, 4, 4, 5, 4],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "revenues.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- link_revenues = uploaded_image.link
- os.remove(imagefile)
-
- embeds = [
- disnake.Embed(
- title=f"**{ticker.upper()} Year Estimates**",
- color=cfg.COLOR,
- ),
- disnake.Embed(
- title=f"**{ticker.upper()} Quarter Earnings**",
- colour=cfg.COLOR,
- ),
- disnake.Embed(
- title=f"**{ticker.upper()} Quarter Revenues**",
- colour=cfg.COLOR,
- ),
- ]
- embeds[0].set_image(url=link_estimates)
- embeds[1].set_image(url=link_earnings)
- embeds[2].set_image(url=link_revenues)
- # Output data
- choices = [
- disnake.SelectOption(
- label=f"{ticker.upper()} Year Estimates", value="0", emoji="🟢"
- ),
- disnake.SelectOption(
- label=f"{ticker.upper()} Quarter Earnings", value="1", emoji="🟢"
- ),
- disnake.SelectOption(
- label=f"{ticker.upper()} Quarter Revenues", value="2", emoji="🟢"
- ),
- ]
-
- await ctx.send(embed=embeds[0], view=Menu(embeds, choices))
- except Exception as e:
- embed = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/pt.py b/discordbot/stocks/due_diligence/pt.py
deleted file mode 100644
index d28c2ded9ef..00000000000
--- a/discordbot/stocks/due_diligence/pt.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import gst_imgur, logger
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.due_diligence import business_insider_model
-
-
-async def pt_command(ctx, ticker: str = "", raw: bool = False, start=""):
- """Displays price targets [Business Insider]"""
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.dd.pt %s", 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 not in [True, False]:
- raise Exception("raw argument has to be true or false")
-
- df_analyst_data = business_insider_model.get_price_target_from_analysts(ticker)
- stock = discordbot.helpers.load(ticker, start)
- print(df_analyst_data)
- 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 = disnake.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:
- plt.style.use("seaborn")
- plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- 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")
- imagefile = "ta_pt.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = discordbot.helpers.autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_pt.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [Business Insider] Price Targets " + ticker
- embed = disnake.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)
diff --git a/discordbot/stocks/due_diligence/sec.py b/discordbot/stocks/due_diligence/sec.py
deleted file mode 100644
index 5027546413f..00000000000
--- a/discordbot/stocks/due_diligence/sec.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-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:
- logger.debug("!stocks.dd.sec %s", 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:
- logger.debug(df_financials.to_string())
-
- df = df_financials
- df.loc[:, "Link"] = "[Link Source](" + df.loc[:, "Link"].astype(str)
- df.loc[:, "Link"] = df.loc[:, "Link"] + ")"
- # Output data
- embed = disnake.Embed(
- title="Stocks: [Market Watch] SEC Filings",
- description=df.to_string(),
- 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 = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/due_diligence/supplier.py b/discordbot/stocks/due_diligence/supplier.py
deleted file mode 100644
index 4d0f21295aa..00000000000
--- a/discordbot/stocks/due_diligence/supplier.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-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:
- logger.debug("!stocks.dd.supplier %s", ticker)
-
- if not ticker:
- raise Exception("A ticker is required")
-
- tickers = csimarket_model.get_suppliers(ticker)
-
- if not tickers:
- raise Exception("Enter a valid ticker")
-
- # Debug user output
- if cfg.DEBUG:
- logger.debug(tickers)
-
- # Output data
- embed = disnake.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 = disnake.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, delete_after=30.0)
diff --git a/discordbot/stocks/government/contracts.py b/discordbot/stocks/government/contracts.py
deleted file mode 100644
index 23f5400f430..00000000000
--- a/discordbot/stocks/government/contracts.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import os
-
-import disnake
-import pandas as pd
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def contracts_command(
- ctx, ticker: str = "", past_transaction_days: int = 10, raw: bool = False
-):
- """Displays contracts associated with tickers [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug(
- "!stocks.gov.contracts %s %s %s", ticker, past_transaction_days, raw
- )
-
- if ticker == "":
- raise Exception("A ticker is required")
-
- # Retrieve Data
- df_contracts = quiverquant_model.get_government_trading("contracts", ticker)
-
- if df_contracts.empty:
- raise Exception("No government contracts found")
-
- # Output Data
- df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]).dt.date
-
- df_contracts = df_contracts[
- df_contracts["Date"].isin(
- df_contracts["Date"].unique()[:past_transaction_days]
- )
- ]
-
- df_contracts.drop_duplicates(inplace=True)
- plt.style.use("seaborn")
-
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax)
- ax.set_ylabel("Amount ($1k)")
- ax.set_title(f"Sum of latest government contracts to {ticker}")
- fig.tight_layout()
-
- plt.savefig("gov_contracts.png")
- imagefile = "gov_contracts.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("gov_contracts.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = f"Stocks: [quiverquant.com] Contracts by {ticker}"
- if raw:
- description = df_contracts.to_string()
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- else:
- embed = disnake.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("gov_contracts.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [quiverquant.com] Contracts by {ticker}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/gtrades.py b/discordbot/stocks/government/gtrades.py
deleted file mode 100644
index ebff7d95fd1..00000000000
--- a/discordbot/stocks/government/gtrades.py
+++ /dev/null
@@ -1,168 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-import matplotlib.dates as mdates
-import pandas as pd
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def gtrades_command(
- ctx,
- ticker: str = "",
- gov_type="",
- past_transactions_months: int = 10,
- raw: bool = False,
-):
- """Displays government trades [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug(
- "!stocks.gov.gtrades %s %s %s %s",
- ticker,
- gov_type,
- past_transactions_months,
- raw,
- )
-
- if ticker == "":
- raise Exception("A ticker is required")
-
- possible_args = ["congress", "senate", "house"]
- if gov_type == "":
- gov_type = "congress"
- elif gov_type not in possible_args:
- raise Exception(
- "Enter a valid government argument, options are: congress, senate and house"
- )
-
- # Retrieve Data
- df_gov = quiverquant_model.get_government_trading(gov_type, ticker)
-
- if df_gov.empty:
- raise Exception(f"No {gov_type} trading data found")
-
- # Output Data
- df_gov = df_gov.sort_values("TransactionDate", ascending=False)
-
- start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
-
- df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
-
- df_gov = df_gov[df_gov["TransactionDate"] > start_date]
-
- if df_gov.empty:
- logger.debug("No recent %s trading data found", gov_type)
- return
-
- df_gov["min"] = df_gov["Range"].apply(
- lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
- )
- df_gov["max"] = df_gov["Range"].apply(
- lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
- if "-" in x
- else x.strip("$").replace(",", "").split("\n")[0]
- )
-
- df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: int(float(x["min"]))
- if x["Transaction"] == "Purchase"
- else -int(float(x["max"])),
- axis=1,
- )
- df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: int(float(x["max"]))
- if x["Transaction"] == "Purchase"
- else -1 * int(float(x["min"])),
- axis=1,
- )
-
- df_gov = df_gov.sort_values("TransactionDate", ascending=True)
- plt.style.use("seaborn")
-
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- ax.fill_between(
- df_gov["TransactionDate"].unique(),
- df_gov.groupby("TransactionDate")["lower"].sum().values / 1000,
- df_gov.groupby("TransactionDate")["upper"].sum().values / 1000,
- )
-
- ax.set_xlim(
- [
- df_gov["TransactionDate"].values[0],
- df_gov["TransactionDate"].values[-1],
- ]
- )
- ax.grid()
- ax.set_title(f"{gov_type.capitalize()} trading on {ticker}")
- ax.set_xlabel("Date")
- ax.set_ylabel("Amount ($1k)")
- plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
- plt.gcf().autofmt_xdate()
- fig.tight_layout()
-
- plt.savefig("gov_gtrades.png")
- imagefile = "gov_gtrades.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("gov_gtrades.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [quiverquant.com] Government Trades"
- if raw:
- description = df_gov.to_string()
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- else:
- embed = disnake.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("gov_gtrades.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [quiverquant.com] Government Trades",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/histcont.py b/discordbot/stocks/government/histcont.py
deleted file mode 100644
index 28926a06f3f..00000000000
--- a/discordbot/stocks/government/histcont.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import os
-
-import disnake
-import numpy as np
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def histcont_command(ctx, ticker=""):
- """Displays historical quarterly-contracts [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.gov.histcont %s", ticker)
-
- if ticker == "":
- raise Exception("A ticker is required")
-
- # Retrieve Data
- df_contracts = quiverquant_model.get_government_trading(
- "quarter-contracts", ticker=ticker
- )
-
- if df_contracts.empty:
- logger.debug("No quarterly government contracts found")
- return
-
- # Output Data
- amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values
-
- qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values
- year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values
-
- quarter_ticks = [
- f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr)
- ]
- plt.style.use("seaborn")
-
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15)
-
- ax.set_xlim([-0.5, len(amounts) - 0.5])
- ax.set_xticks(np.arange(0, len(amounts)))
- ax.set_xticklabels(quarter_ticks)
- ax.grid()
- ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}")
- ax.set_xlabel("Date")
- ax.set_ylabel("Amount ($1k)")
- fig.tight_layout()
-
- plt.savefig("gov_histcont.png")
- imagefile = "gov_histcont.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("gov_histcont.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: Historical Quarterly Government Contract " + ticker
- embed = disnake.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("gov_histcont.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Historical Quarterly Government Contract",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/lastcontracts.py b/discordbot/stocks/government/lastcontracts.py
deleted file mode 100644
index a19f11fb728..00000000000
--- a/discordbot/stocks/government/lastcontracts.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def lastcontracts_command(ctx, past_transactions_days: int = 2, num: int = 20):
- """Displays last government contracts [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.gov.lastcontracts %s %s", past_transactions_days, num)
-
- df_contracts = quiverquant_model.get_government_trading("contracts")
-
- if df_contracts.empty:
- logger.debug("No government contracts found")
- return
-
- df_contracts.sort_values("Date", ascending=False)
-
- df_contracts["Date"] = pd.to_datetime(df_contracts["Date"])
- df_contracts["Date"] = df_contracts["Date"].dt.date
-
- df_contracts.drop_duplicates(inplace=True)
- df_contracts = df_contracts[
- df_contracts["Date"].isin(
- df_contracts["Date"].unique()[:past_transactions_days]
- )
- ]
-
- df_contracts = df_contracts[["Date", "Ticker", "Amount", "Agency"]][:num]
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df_contracts["Ticker"].values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- i += 1
-
- columns = []
- df_contracts = df_contracts.T
- columns.append(
- disnake.Embed(
- title="Stocks: [quiverquant.com] Top buy government trading",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_contracts.columns.values:
- columns.append(
- disnake.Embed(
- description="```"
- + df_contracts[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [quiverquant.com] Top buy government trading",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/lasttrades.py b/discordbot/stocks/government/lasttrades.py
deleted file mode 100644
index 2efbe722355..00000000000
--- a/discordbot/stocks/government/lasttrades.py
+++ /dev/null
@@ -1,189 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def lasttrades_command(ctx, gov_type="", past_days: int = 5, representative=""):
- """Displays trades made by the congress/senate/house [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug(
- "!stocks.gov.lasttrades %s %s %s",
- gov_type,
- past_days,
- representative,
- )
-
- possible_args = ["congress", "senate", "house"]
- if gov_type == "":
- gov_type = "congress"
- elif gov_type not in possible_args:
- raise Exception(
- "Enter a valid government argument, options are: congress, senate and house"
- )
-
- # Retrieve Data
- df_gov = quiverquant_model.get_government_trading(gov_type)
-
- # Output Data
- if df_gov.empty:
- raise Exception(f"No {gov_type} trading data found")
- df_gov = df_gov.sort_values("TransactionDate", ascending=False)
-
- df_gov = df_gov[
- df_gov["TransactionDate"].isin(
- df_gov["TransactionDate"].unique()[:past_days]
- )
- ]
-
- if gov_type == "congress":
- df_gov = df_gov[
- [
- "TransactionDate",
- "Ticker",
- "Representative",
- "Transaction",
- "Range",
- "House",
- "ReportDate",
- ]
- ].rename(
- columns={
- "TransactionDate": "Transaction Date",
- "ReportDate": "Report Date",
- }
- )
- else:
- df_gov = df_gov[
- [
- "TransactionDate",
- "Ticker",
- "Representative",
- "Transaction",
- "Range",
- ]
- ].rename(columns={"TransactionDate": "Transaction Date"})
-
- if representative:
- df_gov_rep = df_gov[
- df_gov["Representative"].str.split().str[0] == representative
- ]
-
- if df_gov_rep.empty:
- raise Exception(
- f"No representative {representative} found in the past {past_days}"
- f" days. The following are available: "
- f"{', '.join(df_gov['Representative'].str.split().str[0].unique())}"
- )
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df_gov_rep["Ticker"].values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
-
- columns = []
- df_gov_rep = df_gov_rep.T
- columns.append(
- disnake.Embed(
- title=f"Stocks: [quiverquant.com] Trades by {representative}",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_gov_rep.columns.values:
- columns.append(
- disnake.Embed(
- description="```"
- + df_gov_rep[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- else:
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = "Overview"
- i = 1
- for col_name in df_gov["Ticker"].values:
- menu = f"\nPage {i}: {col_name}"
- initial_str += f"\nPage {i}: {col_name}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
-
- columns = []
- df_gov = df_gov.T
- columns.append(
- disnake.Embed(
- title=f"Stocks: [quiverquant.com] Trades for {gov_type.upper()}",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- for column in df_gov.columns.values:
- columns.append(
- disnake.Embed(
- description="```"
- + df_gov[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [quiverquant.com] Trades by {gov_type.upper()}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/lobbying.py b/discordbot/stocks/government/lobbying.py
deleted file mode 100644
index 746b4c038f5..00000000000
--- a/discordbot/stocks/government/lobbying.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import disnake
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def lobbying_command(ctx, ticker="", num: int = 10):
- """Displays lobbying details [quiverquant.com]"""
-
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.gov.lobbying %s", ticker)
-
- if ticker == "":
- raise Exception("A ticker is required")
-
- # Retrieve Data
- df_lobbying = quiverquant_model.get_government_trading(
- "corporate-lobbying", ticker=ticker
- )
-
- if df_lobbying.empty:
- logger.debug("No corporate lobbying found")
- return
-
- # Output Data
- report = ""
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- for _, row in (
- df_lobbying.sort_values(by=["Date"], ascending=False).head(num).iterrows()
- ):
- amount = (
- "$" + str(int(float(row["Amount"])))
- if row["Amount"] is not None
- else "N/A"
- )
- report += f"{row['Date']}: {row['Client']} {amount}"
- if row["Amount"] is not None:
- report += "\t" + row["Specific_Issue"].replace("\n", " ").replace(
- "\r", ""
- )
- report += "\n"
-
- if len(report) <= 4000:
- embed = disnake.Embed(
- title=f"Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details",
- description="```" + report + "```",
- 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(report) / 4000:
- columns.append(
- disnake.Embed(
- title=f"Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details",
- description="```" + report[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 ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/qtrcontracts.py b/discordbot/stocks/government/qtrcontracts.py
deleted file mode 100644
index 29cd986e189..00000000000
--- a/discordbot/stocks/government/qtrcontracts.py
+++ /dev/null
@@ -1,158 +0,0 @@
-import os
-
-import disnake
-import numpy as np
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def qtrcontracts_command(ctx, num: int = 20, analysis=""):
- """Displays a look at government contracts [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.gov.qtrcontracts %s %s", num, analysis)
-
- possible_args = ["total", "upmom", "downmom"]
- if analysis == "":
- analysis = "total"
- elif analysis not in possible_args:
- raise Exception(
- "Enter a valid analysis argument, options are: total, upmom and downmom"
- )
-
- # Retrieve Data
- df_contracts = quiverquant_model.get_government_trading("quarter-contracts")
-
- if df_contracts.empty:
- raise Exception("No quarterly government contracts found")
-
- tickers = quiverquant_model.analyze_qtr_contracts(analysis, num)
- plt.style.use("seaborn")
-
- # Output Data
- if analysis in {"upmom", "downmom"}:
- description = tickers.to_string()
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- max_amount = 0
- quarter_ticks = []
- for symbol in tickers:
- amounts = (
- df_contracts[df_contracts["Ticker"] == symbol]
- .sort_values(by=["Year", "Qtr"])["Amount"]
- .values
- )
-
- qtr = (
- df_contracts[df_contracts["Ticker"] == symbol]
- .sort_values(by=["Year", "Qtr"])["Qtr"]
- .values
- )
- year = (
- df_contracts[df_contracts["Ticker"] == symbol]
- .sort_values(by=["Year", "Qtr"])["Year"]
- .values
- )
-
- ax.plot(
- np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15
- )
-
- if len(amounts) > max_amount:
- max_amount = len(amounts)
- quarter_ticks = [
- f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr)
- ]
-
- ax.set_xlim([-0.5, max_amount - 0.5])
- ax.set_xticks(np.arange(0, max_amount))
- ax.set_xticklabels(quarter_ticks)
- ax.grid()
- ax.legend(tickers)
- titles = {
- "upmom": "Highest increasing quarterly Government Contracts",
- "downmom": "Highest decreasing quarterly Government Contracts",
- }
- ax.set_title(titles[analysis])
- ax.set_xlabel("Date")
- ax.set_ylabel("Amount ($1M)")
- fig.tight_layout()
-
- plt.savefig("gov_qtrcontracts.png")
- imagefile = "gov_qtrcontracts.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image(
- "gov_qtrcontracts.png", title="something"
- )
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [quiverquant.com] Government contracts"
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embed.set_image(url=image_link)
- os.remove("gov_qtrcontracts.png")
-
- await ctx.send(embed=embed)
-
- elif analysis == "total":
-
- tickers.index = [ind + " " * (7 - len(ind)) for ind in tickers.index]
- tickers[:] = [str(round(val[0] / 1e9, 2)) for val in tickers.values]
- tickers.columns = ["Amount [M]"]
-
- embed = disnake.Embed(
- title="Stocks: [quiverquant.com] Government contracts",
- description=tickers.to_string(),
- 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 = disnake.Embed(
- title="ERROR Stocks: [quiverquant.com] Government contracts",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/topbuys.py b/discordbot/stocks/government/topbuys.py
deleted file mode 100644
index 7dced8952fc..00000000000
--- a/discordbot/stocks/government/topbuys.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def topbuys_command(
- ctx,
- gov_type="",
- past_transactions_months: int = 5,
- num: int = 20,
- raw: bool = False,
-):
- """Displays most purchased stocks by the congress/senate/house [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug(
- "!stocks.gov.topbuys %s %s %s %s",
- gov_type,
- past_transactions_months,
- num,
- raw,
- )
-
- possible_args = ["congress", "senate", "house"]
- if gov_type == "":
- gov_type = "congress"
- elif gov_type not in possible_args:
- raise Exception(
- "Enter a valid government argument, options are: congress, senate and house"
- )
-
- # Retrieve Data
- df_gov = quiverquant_model.get_government_trading(gov_type)
-
- if df_gov.empty:
- logger.debug("No %s trading data found", gov_type)
- return
-
- df_gov = df_gov.sort_values("TransactionDate", ascending=False)
- start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
-
- df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
-
- df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()
- # Catch bug where error shown for purchase of >5,000,000
- df_gov["Range"] = df_gov["Range"].apply(
- lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x
- )
- df_gov["min"] = df_gov["Range"].apply(
- lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
- )
- df_gov["max"] = df_gov["Range"].apply(
- lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
- if "-" in x
- else x.strip("$").replace(",", "")
- )
- df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: float(x["min"])
- if x["Transaction"] == "Purchase"
- else -float(x["max"]),
- axis=1,
- )
- df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: float(x["max"])
- if x["Transaction"] == "Purchase"
- else -float(x["min"]),
- axis=1,
- )
-
- df_gov = df_gov.sort_values("TransactionDate", ascending=True)
- if raw:
- df = pd.DataFrame(
- df_gov.groupby("Ticker")["upper"]
- .sum()
- .div(1000)
- .sort_values(ascending=False)
- .head(n=num)
- )
- description = "```" + df.to_string() + "```"
-
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values(
- ascending=False
- ).head(n=num).plot(kind="bar", rot=0, ax=ax)
-
- ax.set_ylabel("Amount [1k $]")
- ax.set_title(
- f"Top {num} purchased stocks over last {past_transactions_months} "
- f"months (upper bound) for {gov_type.upper()}"
- )
- plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
- fig.tight_layout()
-
- plt.savefig("gov_topbuys.png")
- imagefile = "gov_topbuys.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("gov_topbuys.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = f"Stocks: [quiverquant.com] Top purchases for {gov_type.upper()}"
- if raw:
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- else:
- embed = disnake.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("gov_topbuys.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [quiverquant.com] Top purchases for {gov_type.upper()}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/toplobbying.py b/discordbot/stocks/government/toplobbying.py
deleted file mode 100644
index ecaef16ade8..00000000000
--- a/discordbot/stocks/government/toplobbying.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-
-import disnake
-import pandas as pd
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def toplobbying_command(ctx, num: int = 10, raw: bool = False):
- """Displays top lobbying firms [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.gov.toplobbying %s %s", num, raw)
-
- # Retrieve Data
- df_lobbying = quiverquant_model.get_government_trading("corporate-lobbying")
-
- if df_lobbying.empty:
- raise Exception("No corporate lobbying found")
-
- df_lobbying["Amount"] = df_lobbying.Amount.astype(float).fillna(0) / 100_000
-
- lobbying_by_ticker = pd.DataFrame(
- df_lobbying.groupby("Ticker")["Amount"].agg("sum")
- ).sort_values(by="Amount", ascending=False)
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- lobbying_by_ticker.head(num).plot(kind="bar", ax=ax)
- ax.set_xlabel("Ticker")
- ax.set_ylabel("Total Amount ($100k)")
- ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}")
- fig.tight_layout()
-
- plt.savefig("ta_toplobbying.png")
- imagefile = "ta_toplobbying.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_toplobbying.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [quiverquant.com] Top Lobbying Firms"
- if raw:
- description = lobbying_by_ticker.head(num).to_string()
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- else:
- embed = disnake.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_toplobbying.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [quiverquant.com] Top Lobbying Firms",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/government/topsells.py b/discordbot/stocks/government/topsells.py
deleted file mode 100644
index 1f97408eb1a..00000000000
--- a/discordbot/stocks/government/topsells.py
+++ /dev/null
@@ -1,173 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.government import quiverquant_model
-
-
-async def topsells_command(
- ctx,
- gov_type="",
- past_transactions_months: int = 5,
- num: int = 10,
- raw: bool = False,
-):
- """Displays most sold stocks by the congress/senate/house [quiverquant.com]"""
- try:
- # Debug user input
- if cfg.DEBUG:
- logger.debug(
- "!stocks.gov.topsells %s %s %s %s",
- gov_type,
- past_transactions_months,
- num,
- raw,
- )
-
- possible_args = ["congress", "senate", "house"]
- if gov_type == "":
- gov_type = "congress"
- elif gov_type not in possible_args:
- raise Exception(
- "Enter a valid government argument, options are: congress, senate and house"
- )
-
- # Retrieve Data
- df_gov = quiverquant_model.get_government_trading(gov_type)
-
- # Output Data
- if df_gov.empty:
- raise Exception(f"No {gov_type} trading data found\n")
-
- df_gov = df_gov.sort_values("TransactionDate", ascending=False)
-
- start_date = datetime.now() - timedelta(days=past_transactions_months * 30)
-
- df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])
-
- df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()
-
- df_gov["Range"] = df_gov["Range"].apply(
- lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x
- )
-
- df_gov["min"] = df_gov["Range"].apply(
- lambda x: x.split("-")[0]
- .strip("$")
- .replace(",", "")
- .strip()
- .replace(">$", "")
- .strip()
- )
- df_gov["max"] = df_gov["Range"].apply(
- lambda x: x.split("-")[1]
- .replace(",", "")
- .strip()
- .strip("$")
- .replace(">$", "")
- .strip()
- if "-" in x
- else x.strip("$").replace(",", "").replace(">$", "").strip()
- )
-
- df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: float(x["min"])
- if x["Transaction"] == "Purchase"
- else -float(x["max"]),
- axis=1,
- )
- df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
- lambda x: float(x["max"])
- if x["Transaction"] == "Purchase"
- else -float(x["min"]),
- axis=1,
- )
-
- df_gov = df_gov.sort_values("TransactionDate", ascending=True)
- if raw:
- df = pd.DataFrame(
- df_gov.groupby("Ticker")["upper"]
- .sum()
- .div(1000)
- .sort_values(ascending=True)
- .abs()
- .head(n=num)
- )
- description = "```" + df.to_string() + "```"
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head(
- n=num
- ).plot(kind="bar", rot=0, ax=ax)
- ax.set_ylabel("Amount ($1k)")
- ax.set_title(
- f"{num} most sold stocks over last {past_transactions_months} months"
- f" (upper bound) for {gov_type}"
- )
- plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
- fig.tight_layout()
-
- plt.savefig("gov_topsells.png")
- imagefile = "gov_topsells.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("gov_topsells.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = f"Stocks: [quiverquant.com] Top sells for {gov_type.upper()}"
- if raw:
- embed = disnake.Embed(
- title=title, description=description, colour=cfg.COLOR
- )
- else:
- embed = disnake.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("gov_topsells.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title=f"ERROR Stocks: [quiverquant.com] Top sells for {gov_type.upper()}",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/insider/lins.py b/discordbot/stocks/insider/lins.py
deleted file mode 100644
index 3385df3ca3e..00000000000
--- a/discordbot/stocks/insider/lins.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import os
-
-import df2img
-import disnake
-import pandas as pd
-from menus.menu import Menu
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.insider import finviz_model
-
-
-async def lins_command(ctx, ticker: str = "", num: int = 10):
- """Display insider activity for a given stock ticker. [Source: Finviz]
-
- Parameters
- ----------
- ticker : Stock Ticker
- num : Number of latest insider activity to display
- """
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug("disc-lins %s", num)
-
- d_finviz_insider = finviz_model.get_last_insider_activity(ticker)
-
- df = pd.DataFrame.from_dict(d_finviz_insider)
- df.set_index("Date", inplace=True)
-
- df = df[
- [
- "Relationship",
- "Transaction",
- "#Shares",
- "Cost",
- "Value ($)",
- "#Shares Total",
- "Insider Trading",
- "SEC Form 4",
- ]
- ]
-
- df = df.head(num)
- df = df.replace(to_replace="Option Exercise", value="Opt Ex.", regex=True)
-
- title = f"Insider Trading for {ticker.upper()}"
-
- embeds: list = []
-
- i, i2, end = 0, 0, 20
- df_pg = []
- embeds_img = []
- dindex = len(df.index)
- while i < dindex:
- df_pg = df.iloc[i:end]
- df_pg.append(df_pg)
- fig = df2img.plot_dataframe(
- df_pg,
- fig_size=(1600, (40 + (40 * 20))),
- col_width=[5, 14, 4, 4, 3, 4, 5, 8, 7],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = f"disc-insider{i}.png"
-
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- image_link = uploaded_image.link
- embeds_img.append(
- f"{image_link}",
- )
- embeds.append(
- disnake.Embed(
- title=title,
- colour=cfg.COLOR,
- ),
- )
- i2 += 1
- i += 20
- end += 20
- os.remove(imagefile)
-
- # Author/Footer
- for i in range(0, i2):
- embeds[i].set_author(
- name=cfg.AUTHOR_NAME,
- url=cfg.AUTHOR_URL,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embeds[i].set_footer(
- text=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- i = 0
- for i in range(0, i2):
- embeds[i].set_image(url=embeds_img[i])
-
- i += 1
- embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
- options = [
- disnake.SelectOption(label="Home", value="0", emoji="🟢"),
- ]
-
- await ctx.send(embed=embeds[0], view=Menu(embeds, options))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Insider Activity [Source: Finviz]",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/options/cc_hist.py b/discordbot/stocks/options/cc_hist.py
deleted file mode 100644
index 1b501bd113c..00000000000
--- a/discordbot/stocks/options/cc_hist.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import os
-import random
-import time
-
-import disnake
-import plotly.graph_objects as go
-import yfinance as yf
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-from discordbot.helpers import autocrop_image
-
-startTime = time.time()
-
-
-async def cc_hist_command(
- ctx, ticker: str = None, expiry: str = "", strike: float = 10, opt_type: str = ""
-):
- """Plot historical option prices
-
- Parameters
- ----------
- ticker: str
- Stock ticker
- expiry: str
- expiration date
- strike: float
- Option strike price
- put: bool
- Calls for call
- Puts for put
- """
- try:
-
- # Debug
- if cfg.DEBUG:
- print(f"opt-hist {ticker} {strike} {opt_type} {expiry}")
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
- yf_ticker = yf.Ticker(ticker)
- dates = list(yf_ticker.options)
-
- if not dates:
- raise Exception("Stock ticker is invalid")
-
- options = yf.Ticker(ticker).option_chain(expiry)
-
- if opt_type == "Calls":
- options = options.calls
- if opt_type == "Puts":
- options = options.puts
-
- chain_id = options.loc[options.strike == strike, "contractSymbol"].values[0]
- df_hist = yf.download(chain_id)
- df_hist.index.name = "date"
-
- plt_title = [
- f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical",
- "Volume",
- ]
- title = f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical"
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.06,
- subplot_titles=plt_title,
- row_width=[0.2, 0.7],
- )
- fig.add_trace(
- go.Candlestick(
- x=df_hist.index,
- open=df_hist.Open,
- high=df_hist.High,
- low=df_hist.Low,
- close=df_hist.Close,
- name="OHLC",
- ),
- row=1,
- col=1,
- )
- colors = [
- "green" if row.Open < row["Close"] else "red"
- for _, row in df_hist.iterrows() # pylint: disable=E1120
- ]
- fig.add_trace(
- go.Bar(
- x=df_hist.index,
- y=df_hist.Volume,
- name="Volume",
- marker_color=colors,
- ),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=25, b=5),
- template=cfg.PLT_CANDLE_STYLE_TEMPLATE,
- showlegend=False,
- yaxis_title="Premium",
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- )
- fig.update_xaxes(
- rangebreaks=[
- dict(bounds=["sat", "mon"]),
- ],
- )
- config = dict({"scrollZoom": True})
- rand = random.randint(69, 69420)
- imagefile = f"opt_hist{rand}.png"
- fig.write_image(imagefile)
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- fig.write_html(f"in/cc_hist_{rand}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cc_hist_{rand}.html)"
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
- if cfg.DEBUG:
- print(f"Image: {imagefile}")
- title = f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: History",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/options/hist.py b/discordbot/stocks/options/hist.py
deleted file mode 100644
index 80019884111..00000000000
--- a/discordbot/stocks/options/hist.py
+++ /dev/null
@@ -1,208 +0,0 @@
-import os
-import random
-import time
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import syncretism_model
-
-startTime = time.time()
-
-
-async def hist_command(
- ctx,
- ticker: str = None,
- expiry: str = "",
- strike: float = 10,
- opt_type: str = "",
- greek: str = "",
-):
- """Plot historical option prices
-
- Parameters
- ----------
- ticker: str
- Stock ticker
- expiry: str
- expiration date
- strike: float
- Option strike price
- put: bool
- Calls for call
- Puts for put
- """
- try:
-
- # Debug
- if cfg.DEBUG:
- print(f"opt-hist {ticker} {strike} {opt_type} {expiry} {greek}")
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
-
- if opt_type == "Puts":
- put = bool(True)
- if opt_type == "Calls":
- put = bool(False)
- chain_id = ""
-
- df_hist = syncretism_model.get_historical_greeks(
- ticker, expiry, chain_id, strike, put
- )
- title = f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical {greek.upper()}"
- # Output data
- fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]])
-
- fig.add_trace(
- go.Scatter(
- name=ticker.upper(),
- x=df_hist.index,
- y=df_hist.price,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- fig.add_trace(
- go.Scatter(
- name="Premium",
- x=df_hist.index,
- y=df_hist["premium"],
- opacity=1,
- yaxis="y2",
- ),
- )
- if greek:
- fig.add_trace(
- go.Scatter(
- name=f"{greek.upper()}",
- x=df_hist.index,
- y=df_hist[greek],
- opacity=1,
- yaxis="y3",
- ),
- )
- fig.update_layout(
- margin=dict(l=10, r=10, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=title,
- title_x=0.03,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- side="right",
- fixedrange=False,
- titlefont=dict(color="#fdc708"),
- tickfont=dict(color="#fdc708"),
- position=0.02,
- nticks=20,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- fixedrange=False,
- domain=[0.037, 1],
- ),
- xaxis2=dict(
- rangeslider=dict(visible=False),
- type="date",
- fixedrange=False,
- ),
- xaxis3=dict(
- rangeslider=dict(visible=False),
- type="date",
- fixedrange=False,
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- yaxis2=dict(
- side="left",
- fixedrange=False,
- anchor="x",
- overlaying="y",
- titlefont=dict(color="#d81aea"),
- tickfont=dict(color="#d81aea"),
- nticks=20,
- ),
- yaxis3=dict(
- side="left",
- position=0,
- fixedrange=False,
- overlaying="y",
- titlefont=dict(color="#00e6c3"),
- tickfont=dict(color="#00e6c3"),
- nticks=20,
- ),
- hovermode="x unified",
- )
- config = dict({"scrollZoom": True})
- rand = random.randint(69, 69420)
- imagefile = f"opt_hist{rand}.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- fig.write_html(f"in/hist_{rand}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hist_{rand}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
- if cfg.DEBUG:
- print(f"Image: {imagefile}")
- title = f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: History",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/options/iv.py b/discordbot/stocks/options/iv.py
deleted file mode 100644
index 16486bc5a6b..00000000000
--- a/discordbot/stocks/options/iv.py
+++ /dev/null
@@ -1,74 +0,0 @@
-import df2img
-import disnake
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import barchart_model
-
-
-async def iv_command(ctx, ticker: str = None):
- """Options IV"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.opt.iv %s", ticker)
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
-
- df = barchart_model.get_options_info(ticker)
- df = df.fillna("")
- df = df.set_axis(
- [
- " ",
- "",
- ],
- axis="columns",
- )
- df.set_index(" ", inplace=True)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(600, 1500),
- col_width=[3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "opt-iv.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = f"{ticker.upper()} Options: IV"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: IV",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/options/oi.py b/discordbot/stocks/options/oi.py
deleted file mode 100644
index 33518a7de43..00000000000
--- a/discordbot/stocks/options/oi.py
+++ /dev/null
@@ -1,188 +0,0 @@
-import os
-import random
-import time
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import op_helpers, yfinance_model
-
-startTime = time.time()
-
-
-async def oi_command(
- ctx,
- ticker: str = None,
- expiry: str = "",
- min_sp: float = None,
- max_sp: float = None,
-):
- """Options OI"""
-
- try:
-
- # Debug
- print(f"!stocks.opt.oi {ticker} {expiry} {min_sp} {max_sp}")
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
-
- dates = yfinance_model.option_expirations(ticker)
-
- if not dates:
- raise Exception("Stock ticker is invalid")
-
- options = yfinance_model.get_option_chain(ticker, expiry)
- calls = options.calls
- puts = options.puts
- current_price = yfinance_model.get_price(ticker)
-
- min_strike = 0.75 * current_price
- max_strike = 1.95 * current_price
-
- if len(calls) > 40:
- min_strike = 0.75 * current_price
- max_strike = 1.25 * current_price
-
- if min_sp:
- min_strike = min_sp
- if max_sp:
- max_strike = max_sp
-
- call_oi = calls.set_index("strike")["openInterest"] / 1000
- put_oi = puts.set_index("strike")["openInterest"] / 1000
-
- df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
- df_opt = df_opt.rename(
- columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
- )
-
- max_pain = op_helpers.calculate_max_pain(df_opt)
- fig = go.Figure()
-
- dmax = df_opt[["OI_call", "OI_put"]].values.max()
- dmin = df_opt[["OI_call", "OI_put"]].values.min()
- fig.add_trace(
- go.Scatter(
- x=df_opt.index,
- y=df_opt["OI_call"],
- name="Calls",
- mode="lines+markers",
- line=dict(color="green", width=3),
- )
- )
-
- fig.add_trace(
- go.Scatter(
- x=df_opt.index,
- y=df_opt["OI_put"],
- name="Puts",
- mode="lines+markers",
- line=dict(color="red", width=3),
- )
- )
- fig.add_trace(
- go.Scatter(
- x=[current_price, current_price],
- y=[dmin, dmax],
- mode="lines",
- line=dict(color="gold", width=2),
- name="Current Price",
- )
- )
- fig.add_trace(
- go.Scatter(
- x=[max_pain, max_pain],
- y=[dmin, dmax],
- mode="lines",
- line=dict(color="grey", width=3, dash="dash"),
- name=f"Max Pain: {max_pain}",
- )
- )
- fig.update_xaxes(
- range=[min_strike, max_strike],
- constrain="domain",
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=60, b=20),
- template=cfg.PLT_SCAT_STYLE_TEMPLATE,
- title=f"Open Interest for {ticker.upper()} expiring {expiry}",
- title_x=0.5,
- legend_title="",
- xaxis_title="Strike",
- yaxis_title="Open Interest (1k)",
- xaxis=dict(
- rangeslider=dict(visible=False),
- ),
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- dragmode="pan",
- )
- config = dict({"scrollZoom": True})
- imagefile = "opt_oi.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/cci_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cci_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- if cfg.DEBUG:
- print(f"Image URL: {imagefile}")
- title = f"Open Interest for {ticker.upper()} expiring {expiry}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: Open Interest",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/options/opt_chain.py b/discordbot/stocks/options/opt_chain.py
deleted file mode 100644
index fc798568d1c..00000000000
--- a/discordbot/stocks/options/opt_chain.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import os
-
-import df2img
-import disnake
-import numpy as np
-import pandas as pd
-from menus.menu import Menu
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import yfinance_model
-
-
-async def chain_command(
- ctx,
- ticker: str = None,
- expiry: str = None,
- opt_type: str = None,
- min_sp: float = None,
- max_sp: float = None,
-):
- """Show calls/puts for given ticker and expiration"""
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "opt-chain %s %s %s %s %s", ticker, expiry, opt_type, min_sp, max_sp
- )
-
- # Check for argument
- if not ticker:
- raise Exception("Stock ticker is required")
-
- dates = yfinance_model.option_expirations(ticker)
-
- if not dates:
- raise Exception("Stock ticker is invalid")
-
- options = yfinance_model.get_option_chain(ticker, str(expiry))
- calls_df = options.calls
- puts_df = options.puts
-
- column_map = {"openInterest": "oi", "volume": "vol", "impliedVolatility": "iv"}
- columns = [
- "strike",
- "bid",
- "ask",
- "volume",
- "openInterest",
- "impliedVolatility",
- ]
-
- if opt_type == "Calls":
- df = calls_df[columns].rename(columns=column_map)
- if opt_type == "Puts":
- df = puts_df[columns].rename(columns=column_map)
-
- min_strike = np.percentile(df["strike"], 1)
- max_strike = np.percentile(df["strike"], 100)
-
- if min_sp:
- min_strike = min_sp
- if max_sp:
- max_strike = max_sp
- if min_sp > max_sp: # type: ignore
- min_sp, max_sp = max_strike, min_strike
-
- df = df[df["strike"] >= min_strike]
- df = df[df["strike"] <= max_strike]
-
- df["iv"] = pd.to_numeric(df["iv"].astype(float))
-
- formats = {"iv": "{:.2f}"}
- for col, f in formats.items():
- df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640
- df.set_index("strike", inplace=True)
-
- title = f"Stocks: {opt_type} Option Chain for {ticker.upper()} on {expiry} [yfinance]"
-
- embeds: list = []
- # Weekly Calls Pages
- i, i2, end = 0, 0, 20
- df_pg = []
- embeds_img = []
- dindex = len(df.index)
- while i < dindex:
- df_pg = df.iloc[i:end]
- df_pg.append(df_pg)
- figp = df2img.plot_dataframe(
- df_pg,
- fig_size=(1000, (40 + (40 * 20))),
- col_width=[3, 3, 3, 3],
- tbl_cells=dict(
- height=35,
- ),
- font=dict(
- family="Consolas",
- size=20,
- ),
- template="plotly_dark",
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = f"opt-chain{i}.png"
-
- df2img.save_dataframe(fig=figp, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- image_link = uploaded_image.link
- embeds_img.append(
- f"{image_link}",
- )
- embeds.append(
- disnake.Embed(
- title=title,
- colour=cfg.COLOR,
- ),
- )
- i2 += 1
- i += 20
- end += 20
- os.remove(imagefile)
-
- # Author/Footer
- for i in range(0, i2):
- embeds[i].set_author(
- name=cfg.AUTHOR_NAME,
- url=cfg.AUTHOR_URL,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embeds[i].set_footer(
- text=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- i = 0
- for i in range(0, i2):
- embeds[i].set_image(url=embeds_img[i])
-
- i += 1
- embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
- options = [
- disnake.SelectOption(label="Home", value="0", emoji="🟢"),
- ]
-
- await ctx.send(embed=embeds[0], view=Menu(embeds, options))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stock-Options: Expirations",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/options/overview.py b/discordbot/stocks/options/overview.py
deleted file mode 100644
index 49ff719afdc..00000000000
--- a/discordbot/stocks/options/overview.py
+++ /dev/null
@@ -1,424 +0,0 @@
-import os
-import random
-import time
-
-import df2img
-import disnake
-import numpy as np
-import pandas as pd
-import plotly.graph_objects as go
-import yfinance as yf
-from menus.menu import Menu
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import op_helpers, yfinance_model
-from gamestonk_terminal.stocks.options.barchart_model import get_options_info
-
-startTime = time.time()
-
-
-# pylint: disable=R0914
-# pylint: disable=R0915
-async def overview_command(
- ctx,
- ticker: str = None,
- expiry: str = None,
- min_sp: float = None,
- max_sp: float = None,
-):
- """Options Overview"""
-
- try:
-
- # Debug
- startTime2 = time.time()
- if cfg.DEBUG:
- print(f"!stocks.opt.iv {ticker} {expiry} {min_sp} {max_sp}")
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
-
- # Get options info/dates, Look for logo_url
- df = get_options_info(ticker) # Barchart Options IV Overview
-
- dates = yfinance_model.option_expirations(ticker) # Expiration dates
- tup = f"{ticker.upper()}"
- url = yf.Ticker(tup).info["logo_url"]
- url += "?raw=true" if url else ""
-
- if not dates:
- raise Exception("Stock ticker is invalid")
-
- options = yfinance_model.get_option_chain(ticker, str(expiry))
- calls = options.calls
- puts = options.puts
- current_price = yfinance_model.get_price(ticker)
-
- min_strike2 = np.percentile(calls["strike"], 1)
- max_strike2 = np.percentile(calls["strike"], 100)
- min_strike = 0.75 * current_price
- max_strike = 1.95 * current_price
-
- if len(calls) > 40:
- min_strike = 0.75 * current_price
- max_strike = 1.25 * current_price
-
- if min_sp:
- min_strike = min_sp
- min_strike2 = min_sp
- if max_sp:
- max_strike = max_sp
- max_strike2 = max_sp
- if min_sp > max_sp: # type: ignore
- min_sp, max_sp = max_strike2, min_strike2
-
- call_oi = calls.set_index("strike")["openInterest"] / 1000
- put_oi = puts.set_index("strike")["openInterest"] / 1000
-
- df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
- df_opt = df_opt.rename(
- columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
- )
-
- max_pain = op_helpers.calculate_max_pain(df_opt)
- fig = go.Figure()
-
- dmax = df_opt[["OI_call", "OI_put"]].values.max()
- dmin = df_opt[["OI_call", "OI_put"]].values.min()
- fig.add_trace(
- go.Scatter(
- x=df_opt.index,
- y=df_opt["OI_call"],
- name="Calls",
- mode="lines+markers",
- line=dict(color="green", width=3),
- )
- )
-
- fig.add_trace(
- go.Scatter(
- x=df_opt.index,
- y=df_opt["OI_put"],
- name="Puts",
- mode="lines+markers",
- line=dict(color="red", width=3),
- )
- )
- fig.add_trace(
- go.Scatter(
- x=[current_price, current_price],
- y=[dmin, dmax],
- mode="lines",
- line=dict(color="gold", width=2),
- name="Current Price",
- )
- )
- fig.add_trace(
- go.Scatter(
- x=[max_pain, max_pain],
- y=[dmin, dmax],
- mode="lines",
- line=dict(color="grey", width=3, dash="dash"),
- name=f"Max Pain: {max_pain}",
- )
- )
- fig.update_xaxes(
- range=[min_strike, max_strike],
- constrain="domain",
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=60, b=20),
- template=cfg.PLT_SCAT_STYLE_TEMPLATE,
- title=f"Open Interest for {ticker.upper()} expiring {expiry}",
- title_x=0.5,
- legend_title="",
- xaxis_title="Strike",
- yaxis_title="Open Interest (1k)",
- xaxis=dict(
- rangeslider=dict(visible=False),
- ),
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- dragmode="pan",
- )
- config = dict({"scrollZoom": True})
- imagefile = "opt_oi.png"
- fig.write_image(imagefile)
-
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/oi_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)"
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
-
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image_oi = gst_imgur.upload_image(imagefile, title="something")
- image_link_oi = uploaded_image_oi.link
-
- column_map = {"openInterest": "oi", "volume": "vol", "impliedVolatility": "iv"}
- columns = [
- "strike",
- "bid",
- "ask",
- "volume",
- "openInterest",
- "impliedVolatility",
- ]
-
- calls_df = calls[columns].rename(columns=column_map)
- puts_df = puts[columns].rename(columns=column_map)
-
- calls_df = calls_df[calls_df["strike"] >= min_strike2]
- calls_df = calls_df[calls_df["strike"] <= max_strike2]
- puts_df = puts_df[puts_df["strike"] >= min_strike2]
- puts_df = puts_df[puts_df["strike"] <= max_strike2]
-
- calls_df["iv"] = pd.to_numeric(calls_df["iv"].astype(float))
- puts_df["iv"] = pd.to_numeric(puts_df["iv"].astype(float))
-
- formats = {"iv": "{:.2f}"}
- for col, f in formats.items():
- calls_df[col] = calls_df[col].map(
- lambda x: f.format(x) # pylint: disable=W0640
- )
- puts_df[col] = puts_df[col].map(
- lambda x: f.format(x) # pylint: disable=W0640
- )
-
- calls_df.set_index("strike", inplace=True)
- puts_df.set_index("strike", inplace=True)
-
- if "-" in df.iloc[0, 1]:
- iv = f"```diff\n- {df.iloc[0, 1]}\n```"
- else:
- iv = f"```yaml\n {df.iloc[0, 1]}\n```"
-
- pfix, sfix = f"{ticker.upper()} ", f" expiring {expiry}"
- if expiry == dates[0]:
- pfix = f"{ticker.upper()} Weekly "
- sfix = ""
-
- embeds = [
- disnake.Embed(
- title=f"{ticker.upper()} Overview",
- color=cfg.COLOR,
- ),
- disnake.Embed(
- title=f"{pfix}Open Interest{sfix}",
- description=plt_link,
- colour=cfg.COLOR,
- ),
- ]
- choices = [
- disnake.SelectOption(
- label=f"{ticker.upper()} Overview", value="0", emoji="🟢"
- ),
- disnake.SelectOption(
- label=f"{pfix}Open Interest{sfix}", value="1", emoji="🟢"
- ),
- ]
- embeds_img = []
-
- i, i2, end = 0, 0, 20
- df_calls = []
- dindex = len(calls_df.index)
- while i <= dindex:
- df_calls = calls_df.iloc[i:end]
- df_calls.append(df_calls)
- figp = df2img.plot_dataframe(
- df_calls,
- fig_size=(1000, (40 + (40 * 20))),
- col_width=[3, 3, 3, 3],
- tbl_cells=cfg.PLT_TBL_CELLS,
- font=cfg.PLT_TBL_FONT,
- template=cfg.PLT_TBL_STYLE_TEMPLATE,
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = f"opt-calls{i}.png"
-
- df2img.save_dataframe(fig=figp, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- image_link = uploaded_image.link
- embeds_img.append(
- f"{image_link}",
- )
- embeds.append(
- disnake.Embed(
- title=f"{pfix}Calls{sfix}",
- colour=cfg.COLOR,
- ),
- )
- i2 += 1
- i += 20
- end += 20
- os.remove(imagefile)
-
- # Add Calls page field
- i, page, puts_page = 2, 0, 3
- i3 = i2 + 2
- choices.append(
- disnake.SelectOption(label="Calls Page 1", value="2", emoji="🟢"),
- )
- for i in range(2, i3):
- page += 1
- puts_page += 1
-
- embeds[i].add_field(name=f"Calls Page {page}", value="_ _", inline=True)
-
- # Puts Pages
- i, end = 0, 20
- df_puts = []
-
- dindex = len(puts_df.index)
- while i <= dindex:
- df_puts = puts_df.iloc[i:end]
- df_puts.append(df_puts)
- figp = df2img.plot_dataframe(
- df_puts,
- fig_size=(1000, (40 + (40 * 20))),
- col_width=[3, 3, 3, 3],
- tbl_cells=cfg.PLT_TBL_CELLS,
- font=cfg.PLT_TBL_FONT,
- template=cfg.PLT_TBL_STYLE_TEMPLATE,
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = f"opt-puts{i}.png"
-
- df2img.save_dataframe(fig=figp, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- uploaded_image = gst_imgur.upload_image(imagefile, title="something")
- image_link = uploaded_image.link
- embeds_img.append(
- f"{image_link}",
- )
- embeds.append(
- disnake.Embed(
- title=f"{pfix}Puts{sfix}",
- colour=cfg.COLOR,
- ),
- )
- i2 += 1
- i += 20
- end += 20
- os.remove(imagefile)
-
- # Add Puts page field
- i, page = 0, 0
- puts_page -= 1
- i2 += 2
- choices.append(
- disnake.SelectOption(label="Puts Page 1", value=f"{puts_page}", emoji="🟢"),
- )
- for i in range(puts_page, i2):
- page += 1
- embeds[i].add_field(name=f"Puts Page {page}", value="_ _", inline=True)
-
- # Author/Footer
- for i in range(0, i2):
- embeds[i].set_author(
- name=cfg.AUTHOR_NAME,
- url=cfg.AUTHOR_URL,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embeds[i].set_footer(
- text=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- # Set images to Pages
- i = 0
- img_i = 0
- embeds[1].set_image(url=image_link_oi)
- for i in range(2, i2):
- embeds[i].set_image(url=embeds_img[img_i])
- img_i += 1
- i += 1
-
- if url:
- embeds[0].set_thumbnail(url=f"{url}")
- else:
- embeds[0].set_thumbnail(url=cfg.AUTHOR_ICON_URL)
-
- # Overview Section
- embeds[0].add_field(name=f"{df.iloc[0, 0]}", value=iv, inline=False)
-
- embeds[0].add_field(
- name=f"•{df.iloc[1, 0]}", value=f"```css\n{df.iloc[1, 1]}\n```", inline=True
- )
- for N in range(2, 6):
- embeds[0].add_field(
- name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
- value=f"```css\n{df.iloc[N, 1]}\n```",
- inline=True,
- )
-
- embeds[0].add_field(name="_ _", value="_ _", inline=False)
- for N in range(6, 8):
- embeds[0].add_field(
- name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
- value=f"```css\n{df.iloc[N, 1]}\n```",
- inline=True,
- )
-
- embeds[0].add_field(name="_ _", value="_ _", inline=False)
- for N in range(8, 10):
- embeds[0].add_field(
- name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
- value=f"```css\n{df.iloc[N, 1]}\n```",
- inline=True,
- )
-
- embeds[0].add_field(name="_ _", value="_ _", inline=False)
- for N in range(10, 12):
- embeds[0].add_field(
- name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
- value=f"```css\n{df.iloc[N, 1]}\n```",
- inline=True,
- )
-
- embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
- executionTime2 = time.time() - startTime2
- print(f"\n> {__name__} is finished: time in seconds: {executionTime2}\n")
- await ctx.send(embed=embeds[0], view=Menu(embeds, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: Overview",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/options/unu.py b/discordbot/stocks/options/unu.py
deleted file mode 100644
index 08fc2b10579..00000000000
--- a/discordbot/stocks/options/unu.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import df2img
-import disnake
-import numpy as np
-import pandas as pd
-import requests
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.helper_funcs import get_user_agent
-
-
-async def unu_command(ctx, num: int = None):
- """Unusual Options"""
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.opt.unu %s", num)
-
- # Check for argument
- if num is None:
- num = 10
-
- pages = np.arange(0, num // 20 + 1)
- data_list = []
- for page_num in pages:
-
- r = requests.get(
- f"https://app.fdscanner.com/api2/unusualvolume?p=0&page_size=20&page={int(page_num)}",
- headers={"User-Agent": get_user_agent()},
- )
-
- if r.status_code != 200:
- logger.debug("Error in fdscanner request")
- return pd.DataFrame(), "request error"
-
- data_list.append(r.json())
-
- ticker, expiry, option_strike, option_type, ask, bid, oi, vol, voi = (
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- )
- for data in data_list:
- for entry in data["data"]:
- ticker.append(entry["tk"])
- expiry.append(entry["expiry"])
- option_strike.append(float(entry["s"]))
- option_type.append("Put" if entry["t"] == "P" else "Call")
- ask.append(entry["a"])
- bid.append(entry["b"])
- oi.append(entry["oi"])
- vol.append(entry["v"])
- voi.append(entry["vol/oi"])
-
- df = pd.DataFrame(
- {
- "Ticker": ticker,
- "Exp": expiry,
- "Strike": option_strike,
- "Type": option_type,
- "Vol/OI": voi,
- "Vol": vol,
- "OI": oi,
- }
- )
-
- df = df.replace({"2021-", "2022-"}, "", regex=True)
- df.set_index("Ticker", inplace=True)
- dindex = len(df.index)
- fig = df2img.plot_dataframe(
- df,
- fig_size=(800, (40 + (40 * dindex))),
- col_width=[3, 3, 3, 3, 3, 3, 3],
- tbl_cells=dict(
- align="left",
- height=35,
- ),
- template="plotly_dark",
- font=dict(
- family="Consolas",
- size=20,
- ),
- paper_bgcolor="rgba(0, 0, 0, 0)",
- )
- imagefile = "opt-unu.png"
- df2img.save_dataframe(fig=fig, filename=imagefile)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
- image = disnake.File(imagefile)
- title = "Unusual Options"
- embed = disnake.Embed(title=title, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Unusual Options",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/options/vol.py b/discordbot/stocks/options/vol.py
deleted file mode 100644
index 9e0dbbb61b8..00000000000
--- a/discordbot/stocks/options/vol.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import os
-import random
-import time
-
-import disnake as discord
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import yfinance_model
-
-startTime = time.time()
-
-
-async def vol_command(
- ctx,
- ticker: str = None,
- expiry: str = "",
- min_sp: float = None,
- max_sp: float = None,
-):
- """Options VOL"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- print(f"opt-vol {ticker} {expiry} {min_sp} {max_sp}")
-
- # Check for argument
- if ticker is None:
- raise Exception("Stock ticker is required")
-
- dates = yfinance_model.option_expirations(ticker)
-
- if not dates:
- raise Exception("Stock ticker is invalid")
-
- options = yfinance_model.get_option_chain(ticker, expiry)
- current_price = yfinance_model.get_price(ticker)
-
- if min_sp is None:
- min_strike = 0.75 * current_price
- else:
- min_strike = min_sp
-
- if max_sp is None:
- max_strike = 1.25 * current_price
- else:
- max_strike = max_sp
-
- calls = options.calls
- puts = options.puts
- call_v = calls.set_index("strike")["volume"] / 1000
- put_v = puts.set_index("strike")["volume"] / 1000
-
- df_opt = pd.merge(put_v, call_v, left_index=True, right_index=True)
- dmax = df_opt.values.max()
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- x=call_v.index,
- y=call_v.values,
- name="Calls",
- mode="lines+markers",
- line=dict(color="green", width=3),
- )
- )
- fig.add_trace(
- go.Scatter(
- x=put_v.index,
- y=put_v.values,
- name="Puts",
- mode="lines+markers",
- line=dict(color="red", width=3),
- )
- )
- fig.add_trace(
- go.Scatter(
- x=[current_price, current_price],
- y=[0, dmax],
- mode="lines",
- line=dict(color="gold", width=2),
- name="Current Price",
- )
- )
- fig.update_xaxes(
- range=[min_strike, max_strike],
- constrain="domain",
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=60, b=20),
- template=cfg.PLT_SCAT_STYLE_TEMPLATE,
- title=f"Volume for {ticker.upper()} expiring {expiry}",
- title_x=0.5,
- legend_title="",
- xaxis_title="Strike",
- yaxis_title="Volume (1k)",
- xaxis=dict(
- rangeslider=dict(visible=False),
- ),
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- dragmode="pan",
- )
- config = dict({"scrollZoom": True})
- imagefile = "opt_vol.png"
- fig.write_image(imagefile)
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/vol_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)"
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = discord.File(imagefile)
-
- if cfg.DEBUG:
- print(f"Image: {imagefile}")
- title = f"Volume for {ticker.upper()} expiring {expiry}"
- embed = discord.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = discord.Embed(
- title="ERROR Options: Volume",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/options/vsurf.py b/discordbot/stocks/options/vsurf.py
deleted file mode 100644
index 6e6094a6fb9..00000000000
--- a/discordbot/stocks/options/vsurf.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import os
-import random
-import time
-
-import disnake
-import numpy as np
-import plotly.graph_objects as go
-from PIL import Image
-from scipy.spatial import Delaunay
-
-import discordbot.config_discordbot as cfg
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.stocks.options import yfinance_model
-
-startTime = time.time()
-
-
-async def vsurf_command(
- ctx,
- ticker: str = "",
- z: str = "IV",
-):
- """Display vol surface
-
- Parameters
- ----------
- ticker: Stock Ticker
- z : The variable for the Z axis
- """
- try:
-
- # Debug
- print(f"!stocks.opt.oi {ticker} {z}")
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- data = yfinance_model.get_iv_surface(ticker)
- if data.empty:
- raise Exception(f"No options data found for {ticker}.\n")
-
- Y = data.dte
- X = data.strike
- if z == "IV":
- Z = data.impliedVolatility
- label = "Volatility"
- elif z == "OI":
- Z = data.openInterest
- label = "Open Interest"
- elif z == "LP":
- Z = data.lastPrice
- label = "Last Price"
-
- points3D = np.vstack((X, Y, Z)).T
- points2D = points3D[:, :2]
- tri = Delaunay(points2D)
- I, J, K = tri.simplices.T
-
- lighting_effects = dict(
- ambient=0.5, diffuse=0.5, roughness=0.5, specular=0.4, fresnel=0.4
- )
- fig = go.Figure(
- data=[
- go.Mesh3d(
- z=Z,
- x=X,
- y=Y,
- i=I,
- j=J,
- k=K,
- intensity=Z,
- colorscale=cfg.PLT_3DMESH_COLORSCALE,
- hovertemplate="DTE: %{y}
Strike: %{x}
"
- + z
- + ": %{z}",
- showscale=False,
- flatshading=True,
- lighting=lighting_effects,
- )
- ]
- )
- fig.update_layout(
- scene=dict(
- xaxis=dict(
- title="Strike",
- tickfont=dict(size=11),
- titlefont=dict(size=12),
- ),
- yaxis=dict(
- title="DTE",
- ),
- zaxis=dict(
- title=z,
- ),
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=40, b=20),
- width=1320,
- height=740,
- template=cfg.PLT_3DMESH_STYLE_TEMPLATE,
- title=f"{label} Surface for {ticker.upper()}",
- title_x=0.5,
- hoverlabel=cfg.PLT_3DMESH_HOVERLABEL,
- scene_camera=dict(
- up=dict(x=0, y=0, z=2),
- center=dict(x=0, y=0, z=-0.3),
- eye=dict(x=1.25, y=1.25, z=0.69),
- ),
- scene=cfg.PLT_3DMESH_SCENE,
- )
- config = dict({"scrollZoom": True})
- imagefile = "opt-vsurf.png"
- fig.write_image(imagefile)
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/vsurf_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vsurf_{html_ran}.html)"
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height
- w = img.width
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- title = f"{label} Surface for {ticker.upper()}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except IndexError:
- embed = disnake.Embed(
- title="ERROR Options: Volatility Surface",
- colour=cfg.COLOR,
- description=f"Inconistent data for {ticker.upper()} {z}",
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Options: Volatility Surface",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
-
-executionTime = time.time() - startTime
-print(f"> Extension {__name__} is ready: time in seconds: {str(executionTime)}\n")
diff --git a/discordbot/stocks/screener/financial.py b/discordbot/stocks/screener/financial.py
deleted file mode 100644
index dffbb74ad1c..00000000000
--- a/discordbot/stocks/screener/financial.py
+++ /dev/null
@@ -1,136 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def financial_command(
- ctx, preset="template", sort="", limit: int = 5, ascend: bool = False
-):
- """Displays returned results from preset by financial metrics [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.financial %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "financial",
- limit,
- ascend,
- )
-
- description = ""
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in so.d_cols_to_sort["financial"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- so.d_cols_to_sort["financial"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['financial'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = description + "Overview"
- i = 1
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Financial Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Financial Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Financial Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/historical.py b/discordbot/stocks/screener/historical.py
deleted file mode 100644
index ec2c07afd81..00000000000
--- a/discordbot/stocks/screener/historical.py
+++ /dev/null
@@ -1,188 +0,0 @@
-import configparser
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import yfinance as yf
-from finvizfinance.screener import ticker
-from matplotlib import pyplot as plt
-from pandas.plotting import register_matplotlib_converters
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.screener import finviz_model
-
-# pylint:disable=no-member
-
-
-async def historical_command(ctx, signal: str = "", start=""):
- """Displays historical price comparison between similar companies [Yahoo Finance]"""
- try:
-
- # Debug user input
- if cfg.DEBUG:
- logger.debug("!stocks.scr.historical %s %s", signal, start)
-
- # Check for argument
- if signal not in so.d_signals_desc:
- raise Exception("Invalid preset selected!")
-
- register_matplotlib_converters()
-
- screen = ticker.Ticker()
-
- if signal in finviz_model.d_signals:
- screen.set_filter(signal=finviz_model.d_signals[signal])
-
- else:
- preset_filter = configparser.RawConfigParser()
- preset_filter.optionxform = str # type: ignore
- preset_filter.read(so.presets_path + signal + ".ini")
-
- d_general = preset_filter["General"]
- d_filters = {
- **preset_filter["Descriptive"],
- **preset_filter["Fundamental"],
- **preset_filter["Technical"],
- }
-
- d_filters = {k: v for k, v in d_filters.items() if v}
-
- if d_general["Signal"]:
- screen.set_filter(filters_dict=d_filters, signal=d_general["Signal"])
- else:
- screen.set_filter(filters_dict=d_filters)
-
- if start == "":
- start = datetime.now() - timedelta(days=365)
- else:
- start = datetime.strptime(start, cfg.DATE_FORMAT)
-
- # Output Data
- l_min = []
- l_leg = []
- l_stocks = screen.ScreenerView(verbose=0)
-
- if len(l_stocks) > 10:
- description = (
- "\nThe limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed."
- "\nThe selected list will be: "
- )
- random.shuffle(l_stocks)
- l_stocks = sorted(l_stocks[:10])
- description = description + (", ".join(l_stocks))
- logger.debug(description)
-
- plt.style.use("seaborn")
- plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
-
- while l_stocks:
- l_parsed_stocks = []
- for symbol in l_stocks:
- try:
- df_similar_stock = yf.download(
- symbol,
- start=datetime.strftime(start, "%Y-%m-%d"),
- progress=False,
- threads=False,
- )
- if not df_similar_stock.empty:
- plt.plot(
- df_similar_stock.index,
- df_similar_stock["Adj Close"].values,
- )
- l_min.append(df_similar_stock.index[0])
- l_leg.append(symbol)
-
- l_parsed_stocks.append(symbol)
- except Exception as e:
- error = (
- f"{e}\nDisregard previous error, which is due to API Rate limits from Yahoo Finance. "
- f"Because we like '{symbol}', and we won't leave without getting data from it."
- )
- embed = disnake.Embed(
- title="ERROR Stocks: [Yahoo Finance] Historical Screener",
- colour=cfg.COLOR,
- description=error,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
-
- for parsed_stock in l_parsed_stocks:
- l_stocks.remove(parsed_stock)
-
- if signal:
- plt.title(
- f"Screener Historical Price using {finviz_model.d_signals[signal]} signal"
- )
- else:
- plt.title(f"Screener Historical Price using {signal} preset")
-
- plt.xlabel("Time")
- plt.ylabel("Share Price ($)")
- plt.legend(l_leg)
- plt.grid(b=True, which="major", color="#666666", linestyle="-")
- plt.minorticks_on()
- plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
- # ensures that the historical data starts from same datapoint
- plt.xlim([max(l_min), df_similar_stock.index[-1]])
-
- plt.savefig("scr_historical.png")
- imagefile = "scr_historical.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("scr_historical.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [Yahoo Finance] Historical Screener"
- embed = disnake.Embed(title=title, description=description, colour=cfg.COLOR)
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embed.set_image(url=image_link)
- os.remove("scr_historical.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Yahoo Finance] Historical Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/overview.py b/discordbot/stocks/screener/overview.py
deleted file mode 100644
index 9e7b6e3ffad..00000000000
--- a/discordbot/stocks/screener/overview.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def overview_command(
- ctx, preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
-):
- """Displays stocks with overview data such as Sector and Industry [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.overview %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "overview",
- limit,
- ascend,
- )
-
- description = ""
-
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in so.d_cols_to_sort["overview"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- so.d_cols_to_sort["overview"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['overview'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = description + "Overview"
- i = 1
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Overview Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Overview Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Overview Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/ownership.py b/discordbot/stocks/screener/ownership.py
deleted file mode 100644
index 57fa72be51b..00000000000
--- a/discordbot/stocks/screener/ownership.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def ownership_command(
- ctx, preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
-):
- """Displays stocks based on own share float and ownership data [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.ownership %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "ownership",
- limit,
- ascend,
- )
-
- description = ""
-
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in so.d_cols_to_sort["ownership"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- so.d_cols_to_sort["ownership"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['ownership'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = description + "Overview"
- i = 1
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Ownership Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Ownership Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Ownership Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/performance.py b/discordbot/stocks/screener/performance.py
deleted file mode 100644
index 4001e1bd35a..00000000000
--- a/discordbot/stocks/screener/performance.py
+++ /dev/null
@@ -1,158 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def performance_command(
- ctx, preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
-):
- """Displays stocks and sort by performance categories [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.performance %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "performance",
- limit,
- ascend,
- )
-
- d_cols_to_sort = {
- "performance": [
- "Ticker",
- "Perf Week",
- "Perf Month",
- "Perf Quart",
- "Perf Half",
- "Perf Year",
- "Perf YTD",
- "Volatility W",
- "Volatility M",
- "Recom",
- "Avg Volume",
- "Rel Volume",
- "Price",
- "Change",
- "Volume",
- ],
- }
-
- description = ""
-
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in d_cols_to_sort["performance"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- d_cols_to_sort["performance"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- "Wrong sort column provided! Provide one of these:"
- f"{', '.join(d_cols_to_sort['performance'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = description + "Overview"
- i = 1
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Performance Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Performance Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Performance Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/presets_custom.py b/discordbot/stocks/screener/presets_custom.py
deleted file mode 100644
index f1379e459a6..00000000000
--- a/discordbot/stocks/screener/presets_custom.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-
-
-async def presets_custom_command(ctx):
- """Displays every custom preset"""
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.scr.presets")
-
- description = ""
- for preset in so.presets:
- with open(
- so.presets_path + preset + ".ini",
- encoding="utf8",
- ) as f:
- preset_line = ""
- for line in f:
- if line.strip() == "[General]":
- break
- preset_line += line.strip()
- description += f"**{preset}:** *{preset_line.split('Description: ')[1].replace('#', '')}*\n"
-
- embed = disnake.Embed(
- title="Stocks: Screener Custom Presets",
- description=description,
- 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 = disnake.Embed(
- title="ERROR Stocks: Screener Presets",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/presets_default.py b/discordbot/stocks/screener/presets_default.py
deleted file mode 100644
index 664e61d37dd..00000000000
--- a/discordbot/stocks/screener/presets_default.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-
-
-async def presets_default_command(ctx):
- """Displays default presets"""
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.scr.presets")
-
- description = ""
- for signame, sigdesc in so.d_signals_desc.items():
- description += f"**{signame}:** *{sigdesc}*\n"
- embed = disnake.Embed(
- title="Stocks: Screener Default Presets",
- description=description,
- 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 = disnake.Embed(
- title="ERROR Stocks: Screener Presets",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/technical.py b/discordbot/stocks/screener/technical.py
deleted file mode 100644
index dfe6c194e01..00000000000
--- a/discordbot/stocks/screener/technical.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def technical_command(
- ctx, preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
-):
- """Displays stocks according to chosen preset, sorting by technical factors [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.technical %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "technical",
- limit,
- ascend,
- )
-
- description = ""
-
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in so.d_cols_to_sort["technical"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- so.d_cols_to_sort["technical"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['technical'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- initial_str = description + "Overview"
- i = 1
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Technical Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Technical Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Technical Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/screener/valuation.py b/discordbot/stocks/screener/valuation.py
deleted file mode 100644
index cc73138d806..00000000000
--- a/discordbot/stocks/screener/valuation.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import difflib
-
-import disnake
-import pandas as pd
-from menus.menu import Menu
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from discordbot.stocks.screener import screener_options as so
-from gamestonk_terminal.stocks.screener.finviz_model import get_screener_data
-
-
-async def valuation_command(
- ctx, preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False
-):
- """Displays results from chosen preset focusing on valuation metrics [Finviz]"""
- try:
- # Check for argument
- if preset == "template" or preset not in so.all_presets:
- raise Exception("Invalid preset selected!")
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.scr.valuation %s %s %s %s", preset, sort, limit, ascend
- )
-
- # Check for argument
- if limit < 0:
- raise Exception("Number has to be above 0")
-
- # Output Data
- df_screen = get_screener_data(
- preset,
- "valuation",
- limit,
- ascend,
- )
-
- description = ""
-
- if isinstance(df_screen, pd.DataFrame):
- if df_screen.empty:
- return []
-
- df_screen = df_screen.dropna(axis="columns", how="all")
-
- if sort:
- if " ".join(sort) in so.d_cols_to_sort["valuation"]:
- df_screen = df_screen.sort_values(
- by=[" ".join(sort)],
- ascending=ascend,
- na_position="last",
- )
- else:
- similar_cmd = difflib.get_close_matches(
- " ".join(sort),
- so.d_cols_to_sort["valuation"],
- n=1,
- cutoff=0.7,
- )
- if similar_cmd:
- description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n"
- df_screen = df_screen.sort_values(
- by=[similar_cmd[0]],
- ascending=ascend,
- na_position="last",
- )
- else:
- raise ValueError(
- f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['valuation'])}"
- )
-
- df_screen = df_screen.fillna("")
- future_column_name = df_screen["Ticker"]
- df_screen = df_screen.head(n=limit).transpose()
- df_screen.columns = future_column_name
- df_screen.drop("Ticker")
-
- columns = []
- choices = [
- disnake.SelectOption(label="Overview", value="0", emoji="🟢"),
- ]
- initial_str = description + "Overview"
- i = 1
- for column in df_screen.columns.values:
- menu = f"\nPage {i}: {column}"
- initial_str += f"\nPage {i}: {column}"
- if i < 19:
- choices.append(
- disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"),
- )
- if i == 20:
- choices.append(
- disnake.SelectOption(
- label="Max Reached", value=f"{i}", emoji="🟢"
- ),
- )
- i += 1
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Valuation Screener",
- description=initial_str,
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
- for column in df_screen.columns.values:
- columns.append(
- disnake.Embed(
- title="Stocks: [Finviz] Valuation Screener",
- description="```"
- + df_screen[column].fillna("").to_string()
- + "```",
- colour=cfg.COLOR,
- ).set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- )
-
- await ctx.send(embed=columns[0], view=Menu(columns, choices))
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Valuation Screener",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/ad.py b/discordbot/stocks/technical_analysis/ad.py
deleted file mode 100644
index 3d72dd95453..00000000000
--- a/discordbot/stocks/technical_analysis/ad.py
+++ /dev/null
@@ -1,200 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import volume_model
-
-
-async def ad_command(ctx, ticker="", is_open="False", start="", end=""):
- """Displays chart with accumulation/distribution line [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.ta.ad %s %s %s %s", ticker, is_open, start, end)
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if is_open in ["false", "False", "FALSE", ""]:
- is_open = False
-
- if is_open in ["true", "True", "TRUE"]:
- is_open = True
-
- if is_open not in [True, False]:
- raise Exception("is_open argument has to be true or false")
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- divisor = 1_000_000
- df_vol = df_stock["Volume"].dropna()
- df_vol = df_vol.values / divisor
- df_ta = volume_model.ad(df_stock, is_open)
- df_cal = df_ta.values
- df_cal = df_cal / divisor
-
- fig = make_subplots(
- rows=3,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.06,
- row_width=[0.2, 0.2, 0.2],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- row=1,
- col=1,
- )
- colors = [
- "green" if row.Open < row["Adj Close"] else "red"
- for _, row in df_stock.iterrows()
- ]
- fig.add_trace(
- go.Bar(
- x=df_stock.index,
- y=df_vol,
- name="Volume [M]",
- marker_color=colors,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name="A/D [M]",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- line=dict(width=2),
- opacity=1,
- ),
- row=3,
- col=1,
- )
- fig.add_hline(
- y=0,
- fillcolor="grey",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="grey", dash="dash"),
- row=3,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=10, r=0, t=40, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} AD",
- title_x=0.4,
- yaxis_title="Stock Price ($)",
- yaxis2_title="Volume [M]",
- yaxis3_title="A/D [M]",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_ad.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/ad_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ad_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: Accumulation/Distribution Line {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Accumulation/Distribution Line",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/adosc.py b/discordbot/stocks/technical_analysis/adosc.py
deleted file mode 100644
index bf577d79ee0..00000000000
--- a/discordbot/stocks/technical_analysis/adosc.py
+++ /dev/null
@@ -1,197 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import volume_model
-
-
-async def adosc_command(
- ctx, ticker="", is_open="False", fast="3", slow="10", start="", end=""
-):
- """Displays chart with chaikin oscillator [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.adosc %s %s %s %s %s",
- ticker,
- is_open,
- fast,
- slow,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not fast.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- fast = float(fast)
- if not slow.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- slow = float(slow)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- # Output Data
- divisor = 1_000_000
- df_vol = df_stock["Volume"].dropna()
- df_vol = df_vol.values / divisor
- df_ta = volume_model.adosc(df_stock, is_open, fast, slow)
- df_cal = df_ta.values
- df_cal = df_cal / divisor
-
- fig = make_subplots(
- rows=3,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.06,
- row_width=[0.2, 0.2, 0.2],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- row=1,
- col=1,
- )
- colors = [
- "green" if row.Open < row["Adj Close"] else "red"
- for _, row in df_stock.iterrows()
- ]
- fig.add_trace(
- go.Bar(
- x=df_stock.index,
- y=df_vol,
- name="Volume",
- marker_color=colors,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name="AD Osc [M]",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- line=dict(width=2),
- opacity=1,
- ),
- row=3,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=10, r=0, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} AD Oscillator",
- title_x=0.3,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_adosc.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/adosc_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/adosc_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Accumulation/Distribution Oscillator " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Accumulation/Distribution Oscillator",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/adx.py b/discordbot/stocks/technical_analysis/adx.py
deleted file mode 100644
index 4c383376289..00000000000
--- a/discordbot/stocks/technical_analysis/adx.py
+++ /dev/null
@@ -1,228 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import trend_indicators_model
-
-
-async def adx_command(
- ctx, ticker="", length="14", scalar="100", drift="1", start="", end=""
-):
- """Displays chart with average directional movement index [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.adx %s %s %s %s %s",
- ticker,
- length,
- scalar,
- drift,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = float(length)
- if not scalar.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- scalar = float(scalar)
- if not drift.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- drift = float(drift)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = trend_indicators_model.adx(
- df_stock["High"],
- df_stock["Low"],
- df_stock["Adj Close"],
- length,
- scalar,
- drift,
- )
-
- # Output Data
- leg_adx = df_ta.columns[0].replace("_", " ")
- neg_di = df_ta.columns[1].replace("_", " ")
- pos_di = df_ta.columns[2].replace("_", " ")
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.5],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- row=1,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- mode="lines",
- name=f"ADX ({leg_adx})",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- line=dict(width=2),
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- mode="lines",
- name=f"+DI ({pos_di})",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- line=dict(width=1),
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- mode="lines",
- name=f"- DI ({neg_di})",
- x=df_ta.index,
- y=df_ta.iloc[:, 2].values,
- opacity=1,
- line=dict(width=1),
- ),
- row=2,
- col=1,
- )
- fig.add_hline(
- y=25,
- fillcolor="grey",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="grey", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"Average Directional Movement Index (ADX) on {ticker}",
- title_x=0.3,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_adx.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/adx_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/adx_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: Average-Directional-Movement-Index {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Average-Directional-Movement-Index",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/aroon.py b/discordbot/stocks/technical_analysis/aroon.py
deleted file mode 100644
index 8fab7071e71..00000000000
--- a/discordbot/stocks/technical_analysis/aroon.py
+++ /dev/null
@@ -1,209 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import trend_indicators_model
-
-
-async def aroon_command(ctx, ticker="", length="25", scalar="100", start="", end=""):
- """Displays chart with aroon indicator [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.aroon %s %s %s %s %s",
- ticker,
- length,
- scalar,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = int(length)
- if not scalar.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- scalar = float(scalar)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = trend_indicators_model.aroon(
- df_stock["High"], df_stock["Low"], length, scalar
- )
-
- # Output Data
- aadown = df_ta.columns[0].replace("_", " ")
- aaup = df_ta.columns[1].replace("_", " ")
- aaosc = df_ta.columns[2].replace("_", " ")
-
- fig = make_subplots(
- rows=3,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.2, 0.2, 0.2],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name=f"Aroon DOWN ({aadown})",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name=f"Aroon UP ({aaup})",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name=f"Aroon OSC ({aaosc})",
- x=df_ta.index,
- y=df_ta.iloc[:, 2].values,
- opacity=1,
- ),
- row=3,
- col=1,
- )
- fig.add_hline(
- y=50,
- fillcolor="grey",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="grey", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"Aroon on {ticker}",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_aroon.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/aroon_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/aroon_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: Aroon-Indicator {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Aroon-Indicator",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/bbands.py b/discordbot/stocks/technical_analysis/bbands.py
deleted file mode 100644
index 5ae5a44b4d6..00000000000
--- a/discordbot/stocks/technical_analysis/bbands.py
+++ /dev/null
@@ -1,219 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import volatility_model
-
-
-async def bbands_command(
- ctx, ticker="", length="5", n_std="2", mamode="sma", start="", end=""
-):
- """Displays chart with bollinger bands [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.bbands %s %s %s %s %s %s",
- ticker,
- length,
- n_std,
- mamode,
- start,
- end,
- )
-
- # Check for argument
- possible_ma = [
- "dema",
- "ema",
- "fwma",
- "hma",
- "linreg",
- "midpoint",
- "pwma",
- "rma",
- "sinwma",
- "sma",
- "swma",
- "t3",
- "tema",
- "trima",
- "vidya",
- "wma",
- "zlma",
- ]
-
- 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = float(length)
- if not n_std.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- n_std = float(n_std)
-
- if mamode not in possible_ma:
- raise Exception("Invalid ma entered")
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = volatility_model.bbands(df_stock["Adj Close"], length, n_std, mamode)
-
- # Output Data
- bbu = df_ta.columns[2].replace("_", " ")
- bbm = df_ta.columns[1].replace("_", " ")
- bbl = df_ta.columns[0].replace("_", " ")
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{bbu}",
- x=df_ta.index,
- y=df_ta.iloc[:, 2].values,
- opacity=1,
- mode="lines",
- line_color="indigo",
- ),
- )
- fig.add_trace(
- go.Scatter(
- name=f"{bbl}",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- mode="lines",
- line_color="indigo",
- fill="tonexty",
- fillcolor="rgba(74, 0, 128, 0.2)",
- ),
- )
- fig.add_trace(
- go.Scatter(
- name=f"{bbm}",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- line=dict(
- width=1.5,
- dash="dash",
- ),
- ),
- )
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=df_stock.index,
- y=df_stock["Adj Close"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} Bollinger Bands ({mamode.upper()})",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_bbands.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/bbands_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/bbands_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: Bollinger-Bands {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Bollinger-Bands",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/cci.py b/discordbot/stocks/technical_analysis/cci.py
deleted file mode 100644
index 79a4eba3924..00000000000
--- a/discordbot/stocks/technical_analysis/cci.py
+++ /dev/null
@@ -1,219 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import momentum_model
-
-
-async def cci_command(ctx, ticker="", length="14", scalar="0.015", start="", end=""):
- """Displays chart with commodity channel index [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.cci %s %s %s %s %s",
- ticker,
- length,
- scalar,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
- # pylint
- try:
- length = int(length)
- except ValueError as e:
- raise Exception("Length has to be an integer") from e
- try:
- scalar = float(scalar)
- except ValueError as e:
- raise Exception("Scalar has to be an integer") from e
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
- df_ta = momentum_model.cci(
- df_stock["High"], df_stock["Low"], df_stock["Adj Close"], length, scalar
- )
-
- dmin = df_ta.values.min()
- dmax = df_ta.values.max()
- # Output Data
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.6],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- showlegend=False,
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=100,
- y1=dmax,
- fillcolor="red",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=-100,
- y1=dmin,
- fillcolor="green",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hline(
- y=-100,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="green", dash="dash"),
- row=2,
- col=1,
- )
- fig.add_hline(
- y=100,
- fillcolor="red",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="red", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} CCI",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_cci.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/cci_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cci_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Commodity-Channel-Index " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Commodity-Channel-Index",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/cg.py b/discordbot/stocks/technical_analysis/cg.py
deleted file mode 100644
index 4ac892252d5..00000000000
--- a/discordbot/stocks/technical_analysis/cg.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-import numpy as np
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import gst_imgur, logger
-from gamestonk_terminal.common.technical_analysis import momentum_model
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-
-
-async def cg_command(ctx, ticker="", length="14", start="", end=""):
- """Displays chart with centre of gravity [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.cg %s %s %s %s",
- ticker,
- length,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = float(length)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
- df_close = df_stock["Adj Close"]
- df_close.columns = ["values"]
- df_ta = momentum_model.cg(df_close, length)
-
- # Output Data
- plt.style.use("seaborn")
- fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI)
- ax = axes[0]
- ax.set_title(f"{ticker} Centre of Gravity")
- ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=1)
- ax.set_xlim(df_stock.index[0], df_stock.index[-1])
- ax.set_ylabel("Share Price ($)")
- ax.grid(b=True, which="major", color="#666666", linestyle="-")
-
- ax2 = axes[1]
- ax2.plot(df_ta.index, df_ta.values, "b", lw=2, label="CG")
- # shift cg 1 bar forward for signal
- signal = df_ta.values
- signal = np.roll(signal, 1)
- ax2.plot(df_ta.index, signal, "g", lw=1, label="Signal")
- ax2.set_xlim(df_stock.index[0], df_stock.index[-1])
- ax2.grid(b=True, which="major", color="#666666", linestyle="-")
-
- plt.gcf().autofmt_xdate()
- fig.tight_layout(pad=1)
- plt.legend()
-
- plt.savefig("ta_cg.png")
- imagefile = "ta_cg.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = discordbot.helpers.autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_cg.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: Center-of-Gravity " + ticker
- embed = disnake.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_cg.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Center-of-Gravity",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/donchian.py b/discordbot/stocks/technical_analysis/donchian.py
deleted file mode 100644
index eea1b124035..00000000000
--- a/discordbot/stocks/technical_analysis/donchian.py
+++ /dev/null
@@ -1,142 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import gst_imgur, logger
-from gamestonk_terminal.common.technical_analysis import volatility_model
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-
-
-async def donchian_command(
- ctx, ticker="", upper_length="25", lower_length="100", start="", end=""
-):
- """Displays chart with donchian channel [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.donchian %s %s %s %s %s",
- ticker,
- upper_length,
- lower_length,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not upper_length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- upper_length = float(upper_length)
- if not lower_length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- lower_length = float(lower_length)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = volatility_model.donchian(
- df_stock["High"], df_stock["Low"], upper_length, lower_length
- )
-
- # Output Data
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- ax.plot(df_stock.index, df_stock["Adj Close"].values, color="k", lw=3)
- ax.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=1.5, label="upper")
- ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--")
- ax.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=1.5, label="lower")
- ax.set_title(f"{ticker} donchian")
- ax.set_xlim(df_stock.index[0], df_stock.index[-1])
- ax.set_xlabel("Time")
- ax.set_ylabel("Price ($)")
-
- ax.legend([ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]])
- ax.fill_between(
- df_ta.index,
- df_ta.iloc[:, 0].values,
- df_ta.iloc[:, 2].values,
- alpha=0.1,
- color="b",
- )
- ax.grid(b=True, which="major", color="#666666", linestyle="-")
-
- plt.gcf().autofmt_xdate()
- fig.tight_layout(pad=1)
-
- plt.legend()
-
- plt.savefig("ta_donchian.png")
- imagefile = "ta_donchian.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = discordbot.helpers.autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_donchian.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: Donchian-Channels " + ticker
- embed = disnake.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_donchian.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Donchian-Channels",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/ema.py b/discordbot/stocks/technical_analysis/ema.py
deleted file mode 100644
index b2a2aeffcf3..00000000000
--- a/discordbot/stocks/technical_analysis/ema.py
+++ /dev/null
@@ -1,186 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def ema_command(ctx, ticker="", window="", offset="", start="", end=""):
- """Displays chart with exponential moving average [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.ema %s %s %s %s %s",
- ticker,
- window,
- offset,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- l_legend = [ticker]
-
- if window == "":
- window = [20, 50]
- else:
- window_temp = list()
- for wind in window.split(","):
- try:
- window_temp.append(float(wind))
- except Exception as e:
- raise Exception("Window needs to be a float") from e
- window = window_temp
-
- if offset == "":
- offset = 0
-
- ticker = ticker.upper()
- stock = discordbot.helpers.load(ticker, start)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- price_df = pd.DataFrame(
- stock["Adj Close"].values, columns=["Price"], index=stock.index
- )
-
- i = 1
- for win in window:
- ema_data = overlap_model.ema(
- values=stock["Adj Close"], length=win, offset=offset
- )
- price_df = price_df.join(ema_data)
- l_legend.append(f"EMA {win}")
- i += 1
-
- # Output Data
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=price_df.index,
- y=price_df["Price"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- for i in range(1, price_df.shape[1]):
- trace_name = price_df.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=price_df.index,
- y=price_df.iloc[:, i],
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} EMA",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_ema.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/ema_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ema_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Exponential-Moving-Average " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Exponential-Moving-Average",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/fib.py b/discordbot/stocks/technical_analysis/fib.py
deleted file mode 100644
index 25f88cb8e1d..00000000000
--- a/discordbot/stocks/technical_analysis/fib.py
+++ /dev/null
@@ -1,140 +0,0 @@
-import os
-from datetime import datetime, timedelta
-
-import disnake
-from matplotlib import pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import gst_imgur, logger
-from gamestonk_terminal import config_plot as cfp
-from gamestonk_terminal.common.technical_analysis import custom_indicators_model
-from gamestonk_terminal.helper_funcs import plot_autoscale
-
-
-async def fib_command(ctx, ticker="", start="", end=""):
- """Displays chart with fibonacci retracement [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.fib %s %s %s",
- ticker,
- start,
- end,
- )
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- if start == "":
- start = datetime.now() - timedelta(days=365)
- f_start = None
- else:
- start = datetime.strptime(start, cfg.DATE_FORMAT)
- f_start = start
-
- if end == "":
- end = datetime.now()
- f_end = None
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
- f_end = None
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- (
- df_fib,
- min_date,
- max_date,
- min_pr,
- max_pr,
- ) = custom_indicators_model.calculate_fib_levels(df_stock, 120, f_start, f_end)
-
- levels = df_fib.Price
- plt.style.use("seaborn")
- fig, ax = plt.subplots(figsize=(plot_autoscale()), dpi=cfp.PLOT_DPI)
-
- ax.plot(df_stock["Adj Close"], "b")
- ax.plot([min_date, max_date], [min_pr, max_pr], c="k")
-
- for i in levels:
- ax.axhline(y=i, c="g", alpha=0.5)
-
- for i in range(5):
- ax.fill_between(df_stock.index, levels[i], levels[i + 1], alpha=0.6)
-
- ax.set_ylabel("Price")
- ax.set_title(f"Fibonacci Support for {ticker.upper()}")
- ax.set_xlim(df_stock.index[0], df_stock.index[-1])
-
- ax1 = ax.twinx()
- ax1.set_ylim(ax.get_ylim())
- ax1.set_yticks(levels)
- ax1.set_yticklabels([0, 0.235, 0.382, 0.5, 0.618, 1])
-
- plt.gcf().autofmt_xdate()
- fig.tight_layout(pad=1)
-
- plt.savefig("ta_fib.png")
- imagefile = "ta_fib.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = discordbot.helpers.autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_fib.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: Fibonacci-Retracement-Levels " + ticker
- str_df_fib = "```" + df_fib.to_string(index=False) + "```"
- embed = disnake.Embed(title=title, colour=cfg.COLOR, description=str_df_fib)
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- embed.set_image(url=image_link)
- os.remove("ta_fib.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Fibonacci-Retracement-Levels",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/fisher.py b/discordbot/stocks/technical_analysis/fisher.py
deleted file mode 100644
index 9abf514bd84..00000000000
--- a/discordbot/stocks/technical_analysis/fisher.py
+++ /dev/null
@@ -1,215 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import momentum_model
-
-
-async def fisher_command(ctx, ticker="", length="14", start="", end=""):
- """Displays chart with fisher transformation [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.ta.fisher %s %s %s %s", ticker, length, start, end)
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = int(length)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = momentum_model.fisher(df_stock["High"], df_stock["Low"], length)
- dmin = df_ta.values.min()
- dmax = df_ta.values.max()
- # Output Data
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.6],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name="Fisher",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name="Signal",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=2,
- y1=dmax,
- fillcolor="red",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=-2,
- y1=dmin,
- fillcolor="green",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hline(
- y=-2,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="green", dash="dash"),
- row=2,
- col=1,
- )
- fig.add_hline(
- y=2,
- fillcolor="red",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="red", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} Fisher Transform",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_fisher.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/fisher_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/fisher_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Fisher-Transform " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Fisher-Transform",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/hma.py b/discordbot/stocks/technical_analysis/hma.py
deleted file mode 100644
index 4d5f57f4549..00000000000
--- a/discordbot/stocks/technical_analysis/hma.py
+++ /dev/null
@@ -1,182 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def hma_command(ctx, ticker="", window="", offset="", start="", end=""):
- """Displays chart with hull moving average [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.hma %s %s %s %s %s",
- ticker,
- window,
- offset,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- l_legend = [ticker]
-
- if window == "":
- window = [20, 50]
- else:
- window_temp = list()
- for wind in window.split(","):
- try:
- window_temp.append(float(wind))
- except Exception as e:
- raise Exception("Window needs to be a float") from e
- window = window_temp
-
- ticker = ticker.upper()
- stock = discordbot.helpers.load(ticker, start)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- price_df = pd.DataFrame(
- stock["Adj Close"].values, columns=["Price"], index=stock.index
- )
- i = 1
- for win in window:
- hma_data = overlap_model.hma(
- values=stock["Adj Close"], length=win, offset=offset
- )
- price_df = price_df.join(hma_data)
- l_legend.append(f"HMA {win}")
- i += 1
-
- # Output Data
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=price_df.index,
- y=price_df["Price"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- for i in range(1, price_df.shape[1]):
- trace_name = price_df.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=price_df.index,
- y=price_df.iloc[:, i],
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} HMA",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_hma.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/hma_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hma_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Hull-Moving-Average " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Hull-Moving-Average",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/kc.py b/discordbot/stocks/technical_analysis/kc.py
deleted file mode 100644
index 379dbf96865..00000000000
--- a/discordbot/stocks/technical_analysis/kc.py
+++ /dev/null
@@ -1,211 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import volatility_model
-
-
-async def kc_command(
- ctx, ticker="", length="20", scalar="2", mamode="sma", offset="0", start="", end=""
-):
- """Displays chart with keltner channel [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.kc %s %s %s %s %s %s %s",
- ticker,
- length,
- scalar,
- mamode,
- offset,
- start,
- end,
- )
-
- # Check for argument
- possible_ma = ["sma", "ema", "wma", "hma", "zlma"]
-
- 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = int(length)
- if not scalar.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- scalar = float(scalar)
- if not offset.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- offset = float(offset)
-
- if mamode not in possible_ma:
- raise Exception("Invalid ma entered")
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = volatility_model.kc(
- df_stock["High"],
- df_stock["Low"],
- df_stock["Adj Close"],
- length,
- scalar,
- mamode,
- offset,
- )
-
- # Output Data
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name="upper",
- x=df_ta.index,
- y=df_ta.iloc[:, 2].values,
- opacity=1,
- mode="lines",
- line_color="indigo",
- showlegend=False,
- ),
- )
- fig.add_trace(
- go.Scatter(
- name="lower",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- mode="lines",
- line_color="indigo",
- fill="tonexty",
- fillcolor="rgba(74, 0, 128, 0.2)",
- showlegend=False,
- ),
- )
- fig.add_trace(
- go.Scatter(
- name="mid",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- line=dict(
- width=1.5,
- dash="dash",
- ),
- ),
- )
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=df_stock.index,
- y=df_stock["Adj Close"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} Keltner Channels ({mamode.upper()})",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_kc.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/kc_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/kc_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Keltner-Channel " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Keltner-Channel",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/macd.py b/discordbot/stocks/technical_analysis/macd.py
deleted file mode 100644
index 0a9142e3d7f..00000000000
--- a/discordbot/stocks/technical_analysis/macd.py
+++ /dev/null
@@ -1,204 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import momentum_model
-
-
-async def macd_command(
- ctx, ticker="", fast="12", slow="26", signal="9", start="", end=""
-):
- """Displays chart with moving average convergence/divergence [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.macd %s %s %s %s %s %s",
- ticker,
- fast,
- slow,
- signal,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not fast.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- fast = float(fast)
- if not slow.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- slow = float(slow)
- if not signal.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- signal = float(signal)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = momentum_model.macd(df_stock["Adj Close"], fast, slow, signal)
- trace_name = df_ta.columns[0].replace("_", " ")
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.6],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- fig.add_trace(
- go.Bar(
- name="MACD Histogram",
- x=df_ta.index,
- y=df_ta.iloc[:, 1].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- mode="lines",
- name="MACD Line",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- mode="lines",
- name="Signal Line",
- x=df_ta.index,
- y=df_ta.iloc[:, 2].values,
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} {trace_name}",
- title_x=0.3,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_macd.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/macd_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/macd_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Moving-Average-Convergence-Divergence " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Moving-Average-Convergence-Divergence",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/obv.py b/discordbot/stocks/technical_analysis/obv.py
deleted file mode 100644
index bbcb2c934e9..00000000000
--- a/discordbot/stocks/technical_analysis/obv.py
+++ /dev/null
@@ -1,187 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import volume_model
-
-
-async def obv_command(ctx, ticker="", start="", end=""):
- """Displays chart with on balance volume [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.obv %s %s %s",
- ticker,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- # Output Data
- divisor = 1_000_000
- df_vol = df_stock["Volume"].dropna()
- df_vol = df_vol.values / divisor
- df_ta = volume_model.obv(df_stock)
- df_cal = df_ta.values
- df_cal = df_cal / divisor
-
- fig = make_subplots(
- rows=3,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.06,
- row_width=[0.2, 0.2, 0.2],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- row=1,
- col=1,
- )
- colors = [
- "green" if row.Open < row["Adj Close"] else "red"
- for _, row in df_stock.iterrows()
- ]
- fig.add_trace(
- go.Bar(
- x=df_stock.index,
- y=df_vol,
- name="Volume [M]",
- marker_color=colors,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name="OBC [M]",
- x=df_ta.index,
- y=df_ta.iloc[:, 0].values,
- line=dict(width=2),
- opacity=1,
- ),
- row=3,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=10, r=0, t=40, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} OBV",
- title_x=0.4,
- yaxis_title="Stock Price ($)",
- yaxis2_title="Volume [M]",
- yaxis3_title="OBV [M]",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
- ),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_obv.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/obv_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/obv_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: On-Balance-Volume {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: On-Balance-Volume",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/recom.py b/discordbot/stocks/technical_analysis/recom.py
deleted file mode 100644
index e30e4b29194..00000000000
--- a/discordbot/stocks/technical_analysis/recom.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.technical_analysis import tradingview_model
-
-
-async def recom_command(ctx, ticker=""):
- """Displays text of a given stocks recommendation based on ta [Tradingview API]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.ta.recom %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- recom = tradingview_model.get_tradingview_recommendation(
- ticker, "america", "", ""
- )
-
- recom = recom[["BUY", "NEUTRAL", "SELL", "RECOMMENDATION"]]
-
- report = "```" + recom.to_string() + "```"
- embed = disnake.Embed(
- title="Stocks: [Tradingview API] Recommendation based on TA",
- 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 = disnake.Embed(
- title="ERROR Stocks: [Tradingview API] Recommendation based on TA",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/rsi.py b/discordbot/stocks/technical_analysis/rsi.py
deleted file mode 100644
index 52ba693fc31..00000000000
--- a/discordbot/stocks/technical_analysis/rsi.py
+++ /dev/null
@@ -1,221 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import momentum_model
-
-
-async def rsi_command(
- ctx, ticker="", length="14", scalar="100", drift="1", start="", end=""
-):
- """Displays chart with relative strength index [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- # pylint: disable=logging-too-many-args
- logger.debug(
- "!stocks.ta.rsi %s %s %s %s %s %s",
- ticker,
- length,
- scalar,
- drift,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not length.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- length = float(length)
- if not scalar.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- scalar = float(scalar)
- if not drift.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- drift = float(drift)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
- df_ta = momentum_model.rsi(df_stock["Adj Close"], length, scalar, drift)
-
- # Output Data
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.6],
- )
- fig.add_trace(
- go.Scatter(
- name=ticker,
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- for i in range(0, df_ta.shape[1]):
- trace_name = df_ta.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=df_ta.index,
- y=df_ta.iloc[:, i],
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=70,
- y1=100,
- fillcolor="red",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=0,
- y1=30,
- fillcolor="green",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hline(
- y=70,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="red", dash="dash"),
- row=2,
- col=1,
- )
- fig.add_hline(
- y=30,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="green", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=20, t=30, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} {trace_name}",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_rsi.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/rsi_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/rsi_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = f"Stocks: Relative-Strength-Index {ticker}"
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Relative-Strength-Index",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/sma.py b/discordbot/stocks/technical_analysis/sma.py
deleted file mode 100644
index b431ab55a86..00000000000
--- a/discordbot/stocks/technical_analysis/sma.py
+++ /dev/null
@@ -1,182 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def sma_command(ctx, ticker="", window="", offset="", start="", end=""):
- """Displays chart with simple moving average [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.sma %s %s %s %s %s",
- ticker,
- window,
- offset,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- l_legend = [ticker]
-
- if window == "":
- window = [20, 50]
- else:
- window_temp = list()
- for wind in window.split(","):
- try:
- window_temp.append(float(wind))
- except Exception as e:
- raise Exception("Window needs to be a float") from e
- window = window_temp
-
- ticker = ticker.upper()
- stock = discordbot.helpers.load(ticker, start)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- price_df = pd.DataFrame(
- stock["Adj Close"].values, columns=["Price"], index=stock.index
- )
- i = 1
- for win in window:
- sma_data = overlap_model.sma(
- values=stock["Adj Close"], length=win, offset=offset
- )
- price_df = price_df.join(sma_data)
- l_legend.append(f"SMA {win}")
- i += 1
-
- # Output Data
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=price_df.index,
- y=price_df["Price"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- for i in range(1, price_df.shape[1]):
- trace_name = price_df.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=price_df.index,
- y=price_df.iloc[:, i],
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} SMA",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_sma.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/sma_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/sma_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- embed = disnake.Embed(
- title=f"Stocks: Simple-Moving-Average {ticker}",
- description=plt_link,
- colour=cfg.COLOR,
- )
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Simple-Moving-Average",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/stoch.py b/discordbot/stocks/technical_analysis/stoch.py
deleted file mode 100644
index b712deef8b8..00000000000
--- a/discordbot/stocks/technical_analysis/stoch.py
+++ /dev/null
@@ -1,238 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import plotly.graph_objects as go
-from PIL import Image
-from plotly.subplots import make_subplots
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import momentum_model
-
-
-async def stoch_command(
- ctx, ticker="", fast_k="14", slow_d="3", slow_k="3", start="", end=""
-):
- """Displays chart with stochastic relative strength average [Yahoo Finance]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.stoch %s %s %s %s %s %s",
- ticker,
- fast_k,
- slow_k,
- slow_d,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- if not fast_k.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- fast_k = int(fast_k)
- if not slow_k.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- slow_k = int(slow_k)
- if not slow_d.lstrip("-").isnumeric():
- raise Exception("Number has to be an integer")
- slow_d = int(slow_d)
-
- ticker = ticker.upper()
- df_stock = discordbot.helpers.load(ticker, start)
- if df_stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)]
-
- df_ta = momentum_model.stoch(
- df_stock["High"],
- df_stock["Low"],
- df_stock["Adj Close"],
- fast_k,
- slow_d,
- slow_k,
- )
-
- fig = make_subplots(
- rows=2,
- cols=1,
- shared_xaxes=True,
- vertical_spacing=0.07,
- row_width=[0.5, 0.7],
- )
- fig.add_trace(
- go.Scatter(
- x=df_stock.index,
- y=df_stock["Adj Close"].values,
- line=dict(color="#fdc708", width=2),
- opacity=1,
- showlegend=False,
- ),
- row=1,
- col=1,
- )
- K = df_ta.columns[0].replace("_", " ")
- D = df_ta.columns[1].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=f"%K {K}",
- x=df_stock.index,
- y=df_ta.iloc[:, 0].values,
- line=dict(width=1.8),
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_trace(
- go.Scatter(
- name=f"%D {D}",
- x=df_stock.index,
- y=df_ta.iloc[:, 1].values,
- line=dict(width=1.8, dash="dash"),
- opacity=1,
- ),
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=80,
- y1=100,
- fillcolor="red",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hrect(
- y0=0,
- y1=20,
- fillcolor="green",
- opacity=0.2,
- layer="below",
- line_width=0,
- row=2,
- col=1,
- )
- fig.add_hline(
- y=80,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="red", dash="dash"),
- row=2,
- col=1,
- )
- fig.add_hline(
- y=20,
- fillcolor="green",
- opacity=1,
- layer="below",
- line_width=3,
- line=dict(color="green", dash="dash"),
- row=2,
- col=1,
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=40, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"Stochastic Relative Strength Index (STOCH RSI) on {ticker}",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_stoch.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/stoch_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/stoch_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- embed = disnake.Embed(
- title=f"Stocks: Stochastic-Relative-Strength-Index {ticker}",
- description=plt_link,
- colour=cfg.COLOR,
- )
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Stochastic-Relative-Strength-Index",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/summary.py b/discordbot/stocks/technical_analysis/summary.py
deleted file mode 100644
index 8641a31929d..00000000000
--- a/discordbot/stocks/technical_analysis/summary.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import disnake
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import logger
-from gamestonk_terminal.stocks.technical_analysis import finbrain_model
-
-
-async def summary_command(ctx, ticker=""):
- """Displays text of a given stocks ta summary [FinBrain API]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.ta.summary %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
-
- report = finbrain_model.get_technical_summary_report(ticker)
- if report:
- report = "```" + report.replace(". ", ".\n") + "```"
- embed = disnake.Embed(
- title="Stocks: [FinBrain API] Summary",
- 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 = disnake.Embed(
- title="ERROR Stocks: [FinBrain API] Summary",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/view.py b/discordbot/stocks/technical_analysis/view.py
deleted file mode 100644
index 10ed036d3c1..00000000000
--- a/discordbot/stocks/technical_analysis/view.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import io
-import os
-
-import disnake
-import matplotlib.pyplot as plt
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-from discordbot.config_discordbot import gst_imgur, logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.config_plot import PLOT_DPI
-from gamestonk_terminal.helper_funcs import plot_autoscale
-from gamestonk_terminal.stocks.technical_analysis import finviz_model
-
-
-async def view_command(ctx, ticker=""):
- """Displays image from Finviz [Finviz]"""
-
- try:
-
- # Debug
- if cfg.DEBUG:
- logger.debug("!stocks.ta.view %s", ticker)
-
- # Check for argument
- if ticker == "":
- raise Exception("Stock ticker is required")
- image_data = finviz_model.get_finviz_image(ticker)
- dataBytesIO = io.BytesIO(image_data)
- im = Image.open(dataBytesIO)
- plt.style.use("seaborn")
-
- fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
- ax.set_axis_off()
- fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
-
- plt.imshow(im)
-
- plt.savefig("ta_view.png")
- imagefile = "ta_view.png"
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
-
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- uploaded_image = gst_imgur.upload_image("ta_view.png", title="something")
- image_link = uploaded_image.link
- if cfg.DEBUG:
- logger.debug("Image URL: %s", image_link)
- title = "Stocks: [Finviz] Trendlines & Data " + ticker
- embed = disnake.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_view.png")
-
- await ctx.send(embed=embed)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: [Finviz] Trendlines & Data",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/wma.py b/discordbot/stocks/technical_analysis/wma.py
deleted file mode 100644
index 35262621be4..00000000000
--- a/discordbot/stocks/technical_analysis/wma.py
+++ /dev/null
@@ -1,185 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def wma_command(ctx, ticker="", window="", offset="", start="", end=""):
- """Displays chart with weighted moving average [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.wma %s %s %s %s %s",
- ticker,
- window,
- offset,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- l_legend = [ticker]
-
- if window == "":
- window = [20, 50]
- else:
- window_temp = list()
- for wind in window.split(","):
- try:
- window_temp.append(float(wind))
- except Exception as e:
- raise Exception("Window needs to be a float") from e
- window = window_temp
-
- if offset == "":
- offset = 0
-
- ticker = ticker.upper()
- stock = discordbot.helpers.load(ticker, start)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- price_df = pd.DataFrame(
- stock["Adj Close"].values, columns=["Price"], index=stock.index
- )
- i = 1
- for win in window:
- wma_data = overlap_model.wma(
- values=stock["Adj Close"], length=win, offset=offset
- )
- price_df = price_df.join(wma_data)
- l_legend.append(f"WMA {win}")
- i += 1
-
- # Output Data
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=price_df.index,
- y=price_df["Price"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- for i in range(1, price_df.shape[1]):
- trace_name = price_df.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=price_df.index,
- y=price_df.iloc[:, i],
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} WMA",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_wma.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/wma_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/wma_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- print(img.size)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- if cfg.DEBUG:
- logger.debug("Image: %s", imagefile)
- title = "Stocks: Weighted-Moving-Average " + ticker
- embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Weighted-Moving-Average",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/stocks/technical_analysis/zlma.py b/discordbot/stocks/technical_analysis/zlma.py
deleted file mode 100644
index 5feb684da1c..00000000000
--- a/discordbot/stocks/technical_analysis/zlma.py
+++ /dev/null
@@ -1,185 +0,0 @@
-import os
-import random
-from datetime import datetime, timedelta
-
-import disnake
-import pandas as pd
-import plotly.graph_objects as go
-from PIL import Image
-
-import discordbot.config_discordbot as cfg
-import discordbot.helpers
-from discordbot.config_discordbot import logger
-from discordbot.helpers import autocrop_image
-from gamestonk_terminal.common.technical_analysis import overlap_model
-
-
-async def zlma_command(ctx, ticker="", window="", offset="", start="", end=""):
- """Displays chart with zero lag moving average [Yahoo Finance]"""
-
- try:
- # Debug
- if cfg.DEBUG:
- logger.debug(
- "!stocks.ta.zlma %s %s %s %s %s",
- ticker,
- window,
- offset,
- start,
- end,
- )
-
- # 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 end == "":
- end = datetime.now()
- else:
- end = datetime.strptime(end, cfg.DATE_FORMAT)
-
- l_legend = [ticker]
-
- if window == "":
- window = [20]
- else:
- window_temp = list()
- for wind in window.split(","):
- try:
- window_temp.append(float(wind))
- except Exception as e:
- raise Exception("Window needs to be a float") from e
- window = window_temp
-
- if offset == "":
- offset = 0
-
- ticker = ticker.upper()
- stock = discordbot.helpers.load(ticker, start)
- if stock.empty:
- raise Exception("Stock ticker is invalid")
-
- # Retrieve Data
- price_df = pd.DataFrame(
- stock["Adj Close"].values, columns=["Price"], index=stock.index
- )
- i = 1
- for win in window:
- zlma_data = overlap_model.zlma(
- values=stock["Adj Close"], length=win, offset=offset
- )
- price_df = price_df.join(zlma_data)
- l_legend.append(f"ZLMA {win}")
- i += 1
-
- # Output Data
- start = start.strftime("%Y-%m-%d")
- end = end.strftime("%Y-%m-%d")
- price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)]
-
- fig = go.Figure()
- fig.add_trace(
- go.Scatter(
- name=f"{ticker}",
- x=price_df.index,
- y=price_df["Price"],
- line=dict(color="#fdc708", width=2),
- opacity=1,
- ),
- )
- for i in range(1, price_df.shape[1]):
- trace_name = price_df.columns[i].replace("_", " ")
- fig.add_trace(
- go.Scatter(
- name=trace_name,
- x=price_df.index,
- y=price_df.iloc[:, i],
- opacity=1,
- ),
- )
- fig.update_layout(
- margin=dict(l=0, r=0, t=50, b=20),
- template=cfg.PLT_TA_STYLE_TEMPLATE,
- colorway=cfg.PLT_TA_COLORWAY,
- title=f"{ticker} ZLMA",
- title_x=0.5,
- yaxis_title="Stock Price ($)",
- xaxis_title="Time",
- yaxis=dict(
- fixedrange=False,
- ),
- xaxis=dict(
- rangeslider=dict(visible=False),
- type="date",
- ),
- dragmode="pan",
- legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
- )
- config = dict({"scrollZoom": True})
- imagefile = "ta_zlma.png"
-
- # Check if interactive settings are enabled
- plt_link = ""
- if cfg.INTERACTIVE:
- html_ran = random.randint(69, 69420)
- fig.write_html(f"in/zlma_{html_ran}.html", config=config)
- plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/zlma_{html_ran}.html)"
-
- fig.update_layout(
- width=800,
- height=500,
- )
- fig.write_image(imagefile)
-
- img = Image.open(imagefile)
- im_bg = Image.open(cfg.IMG_BG)
- h = img.height + 240
- w = img.width + 520
-
- # Paste fig onto background img and autocrop background
- img = img.resize((w, h), Image.ANTIALIAS)
- x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
- y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
- x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
- y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
- img = img.convert("RGB")
- im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
- im_bg.save(imagefile, "PNG", quality=100)
- image = Image.open(imagefile)
- image = autocrop_image(image, 0)
- image.save(imagefile, "PNG", quality=100)
-
- image = disnake.File(imagefile)
-
- print(f"Image {imagefile}")
- embed = disnake.Embed(
- title=f"Stocks: Zero-Lag-Moving-Average {ticker}",
- description=plt_link,
- colour=cfg.COLOR,
- )
- embed.set_image(url=f"attachment://{imagefile}")
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
- os.remove(imagefile)
-
- await ctx.send(embed=embed, file=image)
-
- except Exception as e:
- embed = disnake.Embed(
- title="ERROR Stocks: Zero-Lag-Moving-Average",
- colour=cfg.COLOR,
- description=e,
- )
- embed.set_author(
- name=cfg.AUTHOR_NAME,
- icon_url=cfg.AUTHOR_ICON_URL,
- )
-
- await ctx.send(embed=embed, delete_after=30.0)
diff --git a/discordbot/ticker b/discordbot/ticker
deleted file mode 100644
index bd69cec423a..00000000000
Binary files a/discordbot/ticker and /dev/null differ