コード例 #1
0
def stonk_upcoming_earnings(update: Update, context: CallbackContext):
    # TODO: Optimize this to load all symbols for event lookup in one go instead of per symbol.
    stonks = context.chat_data.get(conf.INTERNALS['stock'], {})
    columns = ['Company', 'Sym.', 'Date', '-days']
    data = []

    if len(stonks) > 0:
        now = datetime.now()

        for k, s in sorted(stonks.items()):
            ue = s.upcoming_earning()
            date = 'N/A'
            days_left = 'N/A'

            if ue:
                date = ue.strftime('%Y-%m-%d')
                days_left = (ue - now).days

            data.append([s.name, s.symbol, date, days_left])

        df = pd.DataFrame(data, columns=columns)
        reply = df.to_string(index=False,
                             formatters={'Company': '{:.10}'.format})
        reply = f'📅 📅 📅\n\n{reply}'
    else:
        reply = '🧻🤲 Watch list is empty.'

    reply_message(update, reply, parse_mode=ParseMode.HTML, pre=True)
コード例 #2
0
def price(update: Update,
          context: CallbackContext,
          reply: bool = True,
          symbols: Union[bool, List[Union[None, str]]] = False):
    if not symbols:
        symbols = parse_symbols(update, context.args)

    if len(symbols) == 0:
        return False

    for symbol in symbols:
        try:
            s = Stonk(symbol)
        except InvalidSymbol:
            reply_symbol_error(update, symbol)

            continue

        p_text = s.details_price_textual()

        if reply:
            reply_message(update, p_text, parse_mode=ParseMode.HTML, pre=True)
        else:
            send_message(context,
                         update.effective_message.chat_id,
                         p_text,
                         parse_mode=ParseMode.HTML,
                         pre=True)
コード例 #3
0
def show_chats(update: Update, context: CallbackContext) -> None:
    """Shows which chats the bot is in"""
    users = ''
    groups = ''
    channels = ''

    for key, user in context.bot_data.setdefault(conf.INTERNALS['users'],
                                                 {}).items():
        users += f'{user.chat.username} ({user.chat.id}),'
    users = users[:-1] if users != '' else 'N/A'

    for key, group in context.bot_data.setdefault(conf.INTERNALS['groups'],
                                                  {}).items():
        groups += f'{group.chat.title} ({group.chat.id}) by {group.cause_user.username} ({group.cause_user.id}),'
    groups = groups[:-1] if groups != '' else 'N/A'

    for key, channel in context.bot_data.setdefault(conf.INTERNALS['channels'],
                                                    {}).items():
        channels += f'{channel.chat.title} ({channel.chat.id}) by {channel.cause_user.username} (' \
                    f'{channel.cause_user.id}),'
    channels = channels[:-1] if channels != '' else 'N/A'
    text = (f'Users: {users}.\n\n'
            f'Groups: {groups}.\n\n'
            f'Channels: {channels}.')

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #4
0
def list_price(update: Update, context: CallbackContext) -> NoReturn:
    stonks = context.chat_data.get(conf.INTERNALS['stock'], {})
    columns = [
        'Sym.', '⬆️ H', '⬇️️ L', '🛬 C', f"±{conf.LOCAL['currency']}", '±%'
    ]
    data = []

    if len(stonks) > 0:
        for k, s in sorted(stonks.items()):
            dp = s.price_daily()

            data.append(
                [s.symbol, dp.high, dp.low, dp.close, dp.diff, dp.percent])

        df = pd.DataFrame(data, columns=columns)
        reply = df.to_string(index=False,
                             formatters={
                                 columns[1]: formatter_conditional_no_dec,
                                 columns[2]: formatter_conditional_no_dec,
                                 columns[3]: formatter_conditional_no_dec,
                                 columns[4]: formatter_conditional_no_dec,
                                 columns[5]: formatter_conditional_no_dec
                             })
        reply = f'🚀 🚀 🚀 📉 📉 📉\n\n{reply}'
    else:
        reply = '🧻🤲 Watch list is empty.'

    reply_message(update, reply, parse_mode=ParseMode.HTML, pre=True)
コード例 #5
0
def stonk_del(update: Update, context: CallbackContext) -> Union[None, bool]:
    symbols = parse_symbols(update, context.args)

    if len(symbols) == 0:
        return False

    for symbol in symbols:
        symbol: str = symbol.upper()
        stonks = context.chat_data.get(conf.INTERNALS['stock'], {})

        if symbol in stonks:
            stonks.pop(symbol, None)
            context.chat_data[conf.INTERNALS['stock']] = stonks
            msg_daily = get_daily_dict(context.chat_data)

            rise = msg_daily.get(
                conf.JOBS['check_rise_fall_day']['dict']['rise'],
                factory_defaultdict())
            fall = msg_daily.get(
                conf.JOBS['check_rise_fall_day']['dict']['fall'],
                factory_defaultdict())

            if rise and len(rise) > 0:
                msg_daily[conf.JOBS['check_rise_fall_day']['dict']
                          ['rise']].pop(symbol, None)

            if fall and len(fall) > 0:
                msg_daily[conf.JOBS['check_rise_fall_day']['dict']
                          ['fall']].pop(symbol, None)

            reply = f'✅ Symbol <b>{symbol}</b> was removed.'
        else:
            reply = f'⚠️ Symbol <b>{symbol}</b> is not in watchlist.'

        reply_message(update, reply, parse_mode=ParseMode.HTML)
コード例 #6
0
def stonk_add(update: Update, context: CallbackContext) -> Union[None, bool]:
    symbols = parse_symbols(update, context.args)

    if len(symbols) == 0:
        return False

    for symbol in symbols:
        s = False

        try:
            s = Stonk(symbol)
        except InvalidSymbol:
            reply_symbol_error(update, symbol)

            return False

        stonks = context.chat_data.get(conf.INTERNALS['stock'], {})

        if s.symbol not in stonks:
            stonks[s.symbol] = s
            context.chat_data[conf.INTERNALS['stock']] = stonks

            reply = f"✅ {s.name} ({s.symbol}; ISIN: {s.isin}) added to watchlist."
        else:
            stonk_list(update, context)
            reply_random_gif(update, 'boring')

            reply = f"⚠️ {s.name} ({s.symbol}; ISIN: {s.isin}) is already in the watchlist."

        reply_message(update, reply)
コード例 #7
0
def orders(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.orders(count)
    text = f'📖 📖 📖\n\n{text}\n\n<a href="https://finance.yahoo.com/most-active">Source</a>'

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #8
0
def losers(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.losers(count)
    text = f"""📉 📉 📉 ({conf.LOCAL['currency']})\n\n{text}\n\n<a href="https://finance.yahoo.com/losers">Source</a>"""

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #9
0
def discovery_websites(update: Update, context: CallbackContext):
    d = Discovery()
    message_html = '🔍 Useful discovery sources:\n'

    for item in d.websites:
        message_html += f"* <a href=\"{item['url']}\">{item['description']}</>\n"

    reply_message(update, message_html, parse_mode=ParseMode.HTML)
コード例 #10
0
def hot_penny(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.hot_pennystocks(count)
    text = f"""🔥 👼 💰 ({conf.LOCAL['currency']})\n\n{text}\n\n<a href="https://www.pennystockflow.com/">Source</a>"""

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #11
0
def high_short(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.high_short(count)
    text = f'🩳 🩳 🩳\n\n{text}\n\n<a href="https://www.highshortinterest.com/">Source</a>'

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #12
0
def low_float(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.low_float(count)
    text = f'🤲 🤲 🤲\n\n{text}\n\n<a href="https://www.lowfloat.com/">Source</a>'

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #13
0
def underval_growth(update: Update, context: CallbackContext):
    count = parse_daily_perf_count(update, context.args)

    d = Discovery()
    text = d.undervalued_growth(count)
    text = f"""👼 🕺 🕺 ({conf.LOCAL['currency']})\n\n{text}\n\n<a
    href="https://finance.yahoo.com/screener/predefined/undervalued_growth_stocks">Source</a>"""

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #14
0
def r_samoyed_coin(update: Update, context: CallbackContext):
    args = parse_reddit(update, context.args)

    if not args:
        return False

    s = RedditAnalysis(context, update)
    result = s.samoyedcoin(args['sort'], args['count'])

    reply_message(update, result, parse_mode=ParseMode.HTML)
コード例 #15
0
def mauerstrassenwetten(update: Update, context: CallbackContext):
    args = parse_reddit(update, context.args)

    if not args:
        return False

    s = RedditAnalysis(context, update)
    result = s.mauerstrassenwetten(args['sort'], args['count'])

    reply_message(update, result, parse_mode=ParseMode.HTML)
コード例 #16
0
def stock_messages(update: Update, context: CallbackContext):
    symbols = parse_symbols(update, context.args)

    if len(symbols) == 0:
        return False

    st = Stocktwits()
    text = st.messages_ticker(symbols)
    result = f'🐣 Stocktwits Messages 💬\n\n{text}'

    reply_message(update, result, parse_mode=ParseMode.HTML)
コード例 #17
0
def bullbear(update: Update, context: CallbackContext):
    symbols = parse_symbols(update, context.args)

    if len(symbols) == 0:
        return False

    st = Stocktwits()
    text = st.bullbear(symbols)
    result = f'🐣 Stocktwits Analysis 🔍\n\n{text}'

    reply_message(update, result, parse_mode=ParseMode.HTML, pre=True)
コード例 #18
0
def help_admin(update: Update, context: CallbackContext) -> NoReturn:
    reply = """Hi admin, I am the STONKS BOT! You are allowed to use the following commands:
* /stonk_clear | /sc -> Clears the watchlist.
* /all_stonk_clear | /asc -> Clears the watchlist of every user.
* /exec_job_check_rise_fall | /ejcrf -> Executes check_rise_fall immediately.
* /all_stonk_clear | /asc -> Clears all stonk lists of all chats.
* /bot_list_all_data | /blad -> Lists internal data storage.
* /show_chats -> Lists all chats.
* /chat_data_reset | /acdr -> Clear all chat data in `bot_data`.
"""

    reply_message(update, reply)
コード例 #19
0
def stonk_list(update: Update, context: CallbackContext) -> NoReturn:
    stonks = context.chat_data.get(conf.INTERNALS['stock'], {})
    reply = ''

    if len(stonks) > 0:
        for k in sorted(stonks.keys()):
            reply += f'💎 {stonks[k].name} ({k})\n'

        reply = reply[0:-1]
    else:
        reply = '🧻🤲 Watch list is empty.'

    reply_message(update, reply)
コード例 #20
0
def help(update: Update, context: CallbackContext) -> NoReturn:
    reply = """Hi ape, I am the STONKS BOT! Try to use the following commands:
* /help | /h -> This help.
Fundamental:
* /chart [<SYMBOLs/ISINs>] | /c -> Plot the last trading day of a stock.
* /price | /p [<SYMBOLs/ISINs>] -> Get details about the stonk price
* /details | /d [<SYMBOLs/ISINs>] -> Shortcut for /chart & /price.
* /stonk_add [<SYMBOLs/ISINs>] | /sa -> Add a stock to the watchlist.
* /stonk_del [<SYMBOLs/ISINs>] | /sd -> Delete a stock from the watchlist.
* /stonk_list | /sl -> Show the watchlist.
* /stonk_clear | /sc -> Clears the watchlist (not allowed in group chats).
* /list_price | /lp -> List watchlist prices.

Discovery:
* /discovery | /di -> Useful infos to find hot stocks.
* /sector_performance | /sp -> Show sector daily performance.
* /upcoming_earnings | /ue -> Show upcoming earning dates.
* /stonk_upcoming_earnings | /sue -> Show upcoming earning dates for watched stonks.
* /gainers (<count>) | /g -> Show daily gainers.
* /losers (<count>) | /l -> Show daily losers.
* /orders (<count>) | /o -> Show daily high volume stonks.
* /high_short (<count>) | /hs -> Show stonks with high short interest.
* /low_float (<count>) | /lf -> Show stonks with low float.
* /hot_penny (<count>) | /hp -> Show hot penny stonks.
* /underval_large (<count>) | /ul -> Show undervalued large cap stonks.
* /underval_growth (<count>) | /ug -> Show undervalued growth stonks.

Sentiment:
* /wallstreetbets (<sort={hot, rising, new} count>) | /wsb -> Show relevant r/wallstreetbets posts.
* /mauerstrassenwetten (<sort={hot, rising, new} count>) | /msw -> Zeige relevante r/mauerstrassenwetten Einträge.
* /investing (<sort={hot, rising, new} count>) | /ri -> Show relevant r/investing posts.
* /rstocks (<sort={hot, rising, new} count>) | /rs -> Show relevant r/stocks posts.
* /gamestop (<sort={hot, rising, new} count>) | /gme -> Show relevant r/gamestop posts.
* /spielstopp (<sort={hot, rising, new} count>) | /rss -> Show relevant r/spielstopp posts.
* /stockmarket (<sort={hot, rising, new} count>) | /rsm -> Show relevant r/stockmarket posts.
* /daytrading (<sort={hot, rising, new} count>) | /rdt -> Show relevant r/daytrading posts.
* /pennystocks (<sort={hot, rising, new} count>) | /rps -> Show relevant r/pennystocks posts.
* /cryptomarkets (<sort={hot, rising, new} count>) | /rcm -> Show relevant r/cryptomarkets posts.
* /satoshistreetbets (<sort={hot, rising, new} count>) | /ssb -> Show relevant r/satoshistreetbets posts.
* /rsamoyedcoin (<sort={hot, rising, new} count>) | /rsc -> Show relevant r/samoyedcoin posts.
* /popular_symbols (<sort={hot, rising, new} count>) | /ps -> Show popular symbols from Reddit.
* /bullbear [<SYMBOLs>] | /bb -> Bull / Bear analysis for chosen symbols.
* /stock_messages [<SYMBOLs>] | /sm -> Get the latest TwitStock messages for chosen symbols.
* /trending_symbols | /ts -> Get the latest trending symbols from TwitStock.
"""

    reply_message(update, reply)
コード例 #21
0
def bot_list_all_data(update: Update, context: CallbackContext):
    user_data = context.dispatcher.user_data
    chat_data = context.dispatcher.chat_data
    bot_data = context.dispatcher.bot_data
    chat_ids = list(
        set(
            list(user_data.keys()) + list(chat_data.keys()) +
            list(bot_data.get(conf.INTERNALS['groups'], {}).keys())))
    bot = context.bot
    result = 'User Info:\n'

    for c_id in chat_ids:
        user_info = bot.get_chat(c_id).to_dict()
        user_info.pop('photo', None)
        user_info = html.escape(formatter_to_json(user_info))

        result += f'{user_info}\n'

    result += f'\nBot Data:\n{formatter_to_json(bot_data)}\n\n'
    result += f'\nChat Data:\n{formatter_to_json(chat_data)}\n\n'
    result += f'\nUser Data:\n{formatter_to_json(user_data)}'

    reply_message(update, result, parse_mode=ParseMode.HTML, pre=True)
コード例 #22
0
    def popular_symbols(self,
                        days: int = 1,
                        limit: int = 30,
                        convert_currency: bool = True) -> str:
        subs = [
            'pennystocks', 'Daytrading', 'StockMarket', 'stocks', 'investing',
            'wallstreetbets', 'mauerstrassenwetten'
        ]
        columns = [
            'Company', 'Symbol', 'Mentions', 'Price', '% 1mo.',
            'Earnings Date', 'Earnings Days Left'
        ]
        data = []
        timestamp_after = int(
            (datetime.today() - timedelta(days=days)).timestamp())
        psaw_api = PushshiftAPI()
        praw_api = self._get_reddit_client()
        tickers = []

        for sub in subs:
            comments = psaw_api.search_submissions(after=timestamp_after,
                                                   subreddit=sub,
                                                   limit=limit,
                                                   filter=['id'])

            for c in comments:
                try:
                    t = praw_api.submission(id=c.id)

                    if not t.removed_by_category and (t.selftext or t.title):
                        tickers += self._find_tickers(t)
                except Exception as e:
                    # TODO: If HTTP 5xx save datetime to bot_data and do not execute for x minutes.
                    msg = '💔 The remote data source is having issues or has blocked me. Try again MUCH later.'

                    if self.update:
                        reply_message(self.update, msg)
                        reply_random_gif(self.update, 'bot broken')

                    raise e

        if len(tickers) > 0:
            now = datetime.now()
            tickers_stats = dict(Counter(tickers))
            tickers_dedup = list(tickers_stats.keys())

            # TODO: Refactor to own method.
            blacklist = [
                'YOLO', 'LMAO', 'LOL', 'MOFO', 'HAHA', 'DFV', 'WSB', 'MSW',
                'CEO', 'CTO', 'CFO', 'CIO', 'USER'
            ]
            tickers_clean = [t for t in tickers_dedup if t not in blacklist]

            history = yf.download(' '.join(tickers_clean),
                                  period='1mo',
                                  prepost=True,
                                  actions=False,
                                  progress=False,
                                  group_by='ticker')
            tickers_not_existing = list(yf.shared._ERRORS.keys())
            tickers_existing = [
                t for t in tickers_clean if t not in tickers_not_existing
            ]
            tickers = yf.Tickers(' '.join(tickers_existing))

            for symbol in tickers_existing:
                try:
                    t = getattr(tickers.tickers, symbol)
                    t_info = t.info
                except:
                    continue

                earnings_date = None
                earnings_days_left = None

                if t.calendar is not None:
                    if len(t.calendar.T) > 0:
                        earnings_date = t.calendar.iloc[:, 0]['Earnings Date']
                        earnings_days_left = (earnings_date - now).days

                price = history[symbol]['Close'][-1]
                perf_1mo = (price / history[symbol]['Open'][0]) - 1
                # TODO: Refactor with stonk.py (extract method or something like this).
                name = t_info.get(
                    'longName',
                    t_info.get('shortName',
                               t_info.get('name', 'ERROR_IN_NAME_RETRIEVAL')))

                data.append([
                    name, symbol, tickers_stats[symbol], price, perf_1mo,
                    earnings_date, earnings_days_left
                ])

            df = pd.DataFrame(data, columns=columns)

            if convert_currency:
                columns_to_convert = [columns[3]]
                df = self.currency.convert_to_currency_df(
                    self.currency_api, df, columns_to_convert)

            df = df.sort_values(by=columns[2], ascending=False)
            result = df[columns[:-1]].to_string(
                header=['Company', 'Sym', '#', 'Price', '% 1mo', 'Earn.📅'],
                index=False,
                formatters={
                    columns[0]: '{:.9}'.format,
                    columns[3]: formatter_round_currency_scalar,
                    columns[4]: formatter_conditional_no_dec,
                    columns[5]: formatter_date
                })
        else:
            result = 'No symbols could be found. Try again later.'

        return result
コード例 #23
0
def stonk_clear(update: Update, context: CallbackContext) -> NoReturn:
    context.chat_data[conf.INTERNALS['stock']] = {}
    # clear_daily_dict(context.chat_data)
    reply = f'🖤 Watch list purged.'

    reply_message(update, reply)
コード例 #24
0
def trending_symbols(update: Update, context: CallbackContext):
    st = Stocktwits()
    text = st.trending()
    result = f'🐣 Stocktwits Trending 🚀\n\n{text}'

    reply_message(update, result, parse_mode=ParseMode.HTML, pre=True)
コード例 #25
0
def upcoming_earnings(update: Update, context: CallbackContext):
    d = Discovery()
    text = d.upcoming_earnings()
    text = f'🗓️ 🗓️ 🗓️\n\n{text}'

    reply_message(update, text, parse_mode=ParseMode.HTML, pre=True)
コード例 #26
0
def popular_symbols(update: Update, context: CallbackContext):
    s = RedditAnalysis(context, update)
    text = s.popular_symbols()
    result = f"💁 💁 💁 ({conf.LOCAL['currency']})\n\n{text}"

    reply_message(update, result, parse_mode=ParseMode.HTML, pre=True)