Refactor Bot (#1326)

* First commit

* Renamed to bot

* change from discordbot to bots imports

* refractor cmds

+ autocomplete helpers
+ updated dps plots/candle
+ quote cmd

* isort fix

* Update helpers.py

* Added first refactor

* Added commands and refactor

* Added description handler

* Through grains

* ta + image border helper

* Added indices

* merged

* Finished econ

* options refactor

* dark_pool_shorts, candle, quote done

* pagination/dropdown logic to helpers + fixes

* dd/disc/insider done + add image logic to helpers

* linting

* removed plt style

* Almost done with Gov

* fixing conflicts

* Finished gov (excpect ones mentioned)

* screener done

* Test bug refactor

* Store changes to switch

* Finished refactor

* Fixed error

* Delete ::40

* Fixed issue

* Fixed black

* finished

* fixes

Co-authored-by: teh_coderer <me@tehcoderer.com>
Co-authored-by: didierlopes.eth <dro.lopes@campus.fct.unl.pt>
This commit is contained in:
Colin Delahunty
2022-02-20 10:36:59 -05:00
committed by GitHub
parent 3c9c2b9223
commit fae93c67ad
204 changed files with 10116 additions and 12894 deletions

View File

@@ -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):

View File

@@ -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):

156
bots/cmds/dps_cmds.py Normal file
View File

@@ -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))

149
bots/cmds/economy_cmds.py Normal file
View File

@@ -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))

View File

@@ -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):

View File

@@ -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")

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -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,
}

70
bots/economy/energy.py Normal file
View File

@@ -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,
}

57
bots/economy/feargreed.py Normal file
View File

@@ -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,
}

63
bots/economy/futures.py Normal file
View File

@@ -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}

69
bots/economy/glbonds.py Normal file
View File

@@ -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}

70
bots/economy/grains.py Normal file
View File

@@ -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,
}

67
bots/economy/indices.py Normal file
View File

@@ -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,
}

69
bots/economy/meats.py Normal file
View File

@@ -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,
}

69
bots/economy/metals.py Normal file
View File

@@ -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,
}

63
bots/economy/overview.py Normal file
View File

@@ -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,
}

120
bots/economy/performance.py Normal file
View File

@@ -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,
}

71
bots/economy/softs.py Normal file
View File

@@ -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,
}

69
bots/economy/usbonds.py Normal file
View File

@@ -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,
}

126
bots/economy/valuation.py Normal file
View File

@@ -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,
}

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -1,5 +1,5 @@
import discord
import discordbot.config_discordbot as cfg
import bots.config_discordbot as cfg
class GenericCommands(discord.ext.commands.Cog):

271
bots/helpers.py Normal file
View File

@@ -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)

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -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",

209
bots/stocks/candle.py Normal file
View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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="<b>Shares</b> [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="<b>Stock Price</b> ($)",
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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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="<b>Position</b> ($)", 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="<b>Net Short Vol.</b> ($)",
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,
}

56
bots/stocks/disc/ford.py Normal file
View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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(),
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

119
bots/stocks/insider/lins.py Normal file
View File

@@ -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,
}

View File

@@ -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,
}

154
bots/stocks/options/hist.py Normal file
View File

@@ -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="<b>Stock Price</b> ($)",
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,
}

50
bots/stocks/options/iv.py Normal file
View File

@@ -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,
}

137
bots/stocks/options/oi.py Normal file
View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

119
bots/stocks/options/vol.py Normal file
View File

@@ -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,
}

View File

@@ -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="<b>DTE</b>: %{y} <br><b>Strike</b>: %{x} <br><b>"
+ z
+ "</b>: %{z}<extra></extra>",
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,
}

40
bots/stocks/quote.py Normal file
View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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", ""

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

Some files were not shown because too many files have changed in this diff Show More