Source code for util.formatting

# Standard libaries
import datetime
from math import floor, log

import discord

# Third party libraries
import pandas as pd

from constants.sources import data_sources


[docs] def format_change(change: float) -> str: """ Converts a float to a string with a plus sign if the float is positive, and a minus sign if the float is negative. Parameters ---------- change : float The percentual change of an asset. Returns ------- str The formatted change. """ if change is None: return "N/A" if isinstance(change, str): # Try to convert to float try: change = float(change) except ValueError: change = 0 # Round to 2 decimals change = round(change, 2) return f"+{change}% 📈" if change > 0 else f"{change}% 📉"
[docs] def human_format(number: float, absolute: bool = False, decimals: int = 0) -> str: """ Takes a number and returns a human readable string. Taken from: https://stackoverflow.com/questions/579310/formatting-long-numbers-as-strings-in-python/45846841. Parameters ---------- number : float The number to be formatted. absolute : bool If True, the number will be converted to its absolute value. decimals : int The number of decimals to be used. Returns ------- str The formatted number as a string. """ # Try to convert to float if isinstance(number, str): try: number = float(number) except ValueError: number = 0 if number == 0: return "0" # https://idlechampions.fandom.com/wiki/Large_number_abbreviations units = ["", "K", "M", "B", "t", "q"] k = 1000.0 magnitude = int(floor(log(abs(number), k))) if decimals > 0: rounded_number = round(number / k**magnitude, decimals) else: rounded_number = int(number / k**magnitude) if absolute: rounded_number = abs(rounded_number) return f"{rounded_number}{units[magnitude]}"
[docs] def format_embed_length(data: list) -> list: """ If the length of the data is greater than 1024 characters, it will be shortened to that amount. Parameters ---------- data : list The list containing the description for an embed. Returns ------- list The shortened description. """ for x in range(len(data)): if len(data[x]) > 1024: data[x] = data[x][:1024].split("\n")[:-1] # Fix everything that is not x for y in range(len(data)): if x != y: data[y] = "\n".join(data[y].split("\n")[: len(data[x])]) data[x] = "\n".join(data[x]) return data
# Used in gainers, losers loops
[docs] async def format_embed(og_df: pd.DataFrame, type: str, source: str) -> discord.Embed: """ Formats the dataframe to an embed. Parameters ---------- df : pd.DataFrame A dataframe with the columns: Symbol Price % Change Volume type : str The type used in the title of the embed source : str The source used for this data Returns ------- discord.Embed A Discord embed containing the formatted data """ df = og_df.copy() if source == "binance": url = "https://www.binance.com/en/altcoins/gainers-losers" color = data_sources["binance"]["color"] icon_url = data_sources["binance"]["icon"] name = "Coin" elif source == "yahoo": url = "https://finance.yahoo.com/most-active" color = data_sources["yahoo"]["color"] icon_url = data_sources["yahoo"]["icon"] name = "Stock" elif source == "coingecko": url = "https://www.coingecko.com/en/watchlists/trending-crypto" color = data_sources["coingecko"]["color"] icon_url = data_sources["coingecko"]["icon"] name = "Coin" elif source == "coinmarketcap": url = "https://coinmarketcap.com/trending-cryptocurrencies/" color = data_sources["coinmarketcap"]["color"] icon_url = data_sources["coinmarketcap"]["icon"] name = "Coin" elif source.startswith("tradingview"): color = data_sources["tradingview"]["color"] icon_url = data_sources["tradingview"]["icon"] name = "Stock" if source == "tradingview-premarket": url = "https://www.tradingview.com/markets/stocks-usa/market-movers-active-pre-market-stocks/" elif source == "tradingview-afterhours": url = "https://www.tradingview.com/markets/stocks-usa/market-movers-active-after-hours-stocks/" e = discord.Embed( title=f"Top {len(df)} {type}", url=url, description="", color=color, timestamp=datetime.datetime.now(datetime.timezone.utc), ) if source == "yahoo": # Format the data df.rename( columns={ "symbol": "Symbol", "regularMarketPrice": "Price", "regularMarketChange": "% Change", "regularMarketVolume": "Volume", }, inplace=True, ) # Add website to symbol df["Symbol"] = ( "[" + df["Symbol"] + "](https://finance.yahoo.com/quote/" + df["Symbol"] + ")" ) # Only these columns are necessary df = df[["Symbol", "Price", "% Change", "Volume"]] if not source.startswith("tradingview"): df = df.astype( {"Symbol": str, "Price": float, "% Change": float, "Volume": float} ) df = df.round({"Price": 3, "% Change": 2, "Volume": 0}) else: df = df.astype( {"Symbol": str, "Price": float, "% Change": float, "Volume": str} ) df = df.round({"Price": 3, "% Change": 2}) # Apply format_change df["% Change"] = df["% Change"].apply(format_change) # Post symbol, current price (weightedAvgPrice) + change, volume df["Price"] = "$" + df["Price"].astype(str) + " (" + df["% Change"] + ")" # Format volume if it is not done already if not source.startswith("tradingview"): df["Volume"] = df["Volume"].apply(lambda x: "$" + human_format(x)) ticker = "\n".join(df["Symbol"].tolist()) prices = "\n".join(df["Price"].tolist()) vol = "\n".join(df["Volume"].astype(str).tolist()) # Prevent possible overflow ticker, prices, vol = format_embed_length([ticker, prices, vol]) e.add_field( name=name, value=ticker, inline=True, ) e.add_field( name="Price", value=prices, inline=True, ) e.add_field( name="Volume", value=vol, inline=True, ) # Set empty text as footer, so we can see the icon e.set_footer(text="\u200b", icon_url=icon_url) return e