Text Generation
English
opsiie
artificial-intelligence
self-centered-intelligence
sci
ai-assistant
multi-modal
image-generation
video-generation
music-generation
conversational-ai
blockchain
web3
facial-recognition
voice-synthesis
bioinformatics
financial-intelligence
text-classification
sentiment-analysis
token-classification
ner
question-answering
fill-mask
summarization
translation
text2text-generation
zero-shot-classification
image-classification
image-segmentation
object-detection
image-to-text
text-to-image
image-to-image
audio-classification
automatic-speech-recognition
text-to-speech
video-classification
depth-estimation
document-question-answering
visual-question-answering
zero-shot-image-classification
zero-shot-audio-classification
zero-shot-object-detection
feature-extraction
image-feature-extraction
mask-generation
table-question-answering
text-to-audio
| import numpy as np | |
| import yfinance as yf | |
| from datetime import datetime, timedelta | |
| import pandas as pd | |
| from colorama import Fore, Style | |
| import re | |
| import time | |
| import statsmodels.api as sm # New import for ARIMA model | |
| from pytz import timezone | |
| # Native Modules | |
| from markets_mappings import keyword_mapping | |
| # Create a reverse mapping for case-insensitive lookup | |
| reverse_mapping = {ticker.lower(): name for name, ticker in keyword_mapping['companies'].items()} | |
| def handle_markets_command(command): | |
| # Ensure the command starts with /markets and extract the rest | |
| if not command.lower().startswith('/markets'): | |
| return Fore.RED + "Error: Command must start with '/markets'." | |
| # Remove the '/markets' prefix and strip any extra whitespace | |
| command = command[len('/markets'):].strip() | |
| sectors = keyword_mapping['sectors'] | |
| companies = keyword_mapping['companies'] | |
| currencies = keyword_mapping['currencies'] | |
| cryptocurrencies = keyword_mapping['cryptocurrencies'] | |
| # Split the remaining command into parts | |
| parts = command.lower().split() | |
| # Check if at least one keyword is provided | |
| if len(parts) < 1: | |
| return Fore.YELLOW + "Please provide a company, sector, currency, or crypto after '/markets'." | |
| keyword = parts[0] | |
| extra = parts[1] if len(parts) > 1 else None | |
| # Handle compare command | |
| if keyword == 'compare' and len(parts) == 3: | |
| stock1_input = parts[1] | |
| stock2_input = parts[2] | |
| return stock_compare(stock1_input, stock2_input) | |
| # Handle the new oil full report | |
| if keyword == 'oil' and extra == 'full': | |
| return generate_oil_market_report() | |
| # Handle sector commands | |
| if keyword in sectors: | |
| tickers = sectors[keyword] | |
| return display_sector_data(keyword, tickers) | |
| # Handle currency and cryptocurrency commands | |
| if keyword in currencies: | |
| return display_currency_data(keyword, currencies[keyword]) | |
| elif keyword in cryptocurrencies: | |
| return display_crypto_data(keyword, cryptocurrencies[keyword]) | |
| # Handle company commands | |
| elif keyword in companies: | |
| ticker = companies[keyword] | |
| # Check if there's an extra command | |
| if extra: | |
| return handle_company_extra(keyword, ticker, extra) | |
| return display_company_data(keyword, ticker) | |
| # If the keyword is not recognized, return an error message | |
| return Fore.RED + f"Error: The company, sector, currency, or crypto '{keyword}' is not recognized. Please check the name and try again." | |
| def handle_company_extra(company_name, ticker, extra): | |
| if not extra: | |
| return display_company_data(company_name, ticker) | |
| else: | |
| extra = extra.lower() | |
| try: | |
| if extra == 'statistics': | |
| return display_statistics(ticker) | |
| elif extra == 'history': | |
| return display_history(ticker) | |
| elif extra == 'profile': | |
| return display_profile(ticker) | |
| elif extra == 'financials': | |
| return display_financials(ticker) | |
| elif extra == 'analysis': | |
| return display_analysis(ticker) | |
| elif extra == 'options': | |
| return display_options(ticker) | |
| elif extra == 'holders': | |
| return display_holders(ticker) | |
| elif extra == 'sustainability': | |
| return display_sustainability(ticker) | |
| else: | |
| return Fore.RED + f"Invalid extra command: {extra}" | |
| except Exception as e: | |
| return Fore.RED + f"An error occurred while processing the command: {str(e)}" | |
| def display_sector_data(sector_name, tickers): | |
| output = f"\nTop stocks in the {Fore.LIGHTYELLOW_EX}{sector_name.capitalize()} sector{Fore.RESET}:\n\n" | |
| for idx, ticker in enumerate(tickers, start=1): | |
| stock = yf.Ticker(ticker) | |
| hist = stock.history(period="1y") | |
| data = { | |
| '1D': get_percentage_change(hist, days=1), | |
| '5D': get_percentage_change(hist, days=5), | |
| '1M': get_percentage_change(hist, days=30), | |
| '1Y': get_percentage_change(hist, days=365) | |
| } | |
| # Company name in light yellow | |
| output += Fore.LIGHTYELLOW_EX + f"{idx}. {stock.info.get('shortName', ticker)} ({ticker}){Fore.RESET}\n" | |
| # Performance data | |
| performance_line = ( | |
| Fore.WHITE + f" 1D: {format_percentage_change(data['1D'])} | " + | |
| Fore.WHITE + f"5D: {format_percentage_change(data['5D'])} | " + | |
| Fore.WHITE + f"1M: {format_percentage_change(data['1M'])} | " + | |
| Fore.WHITE + f"1Y: {format_percentage_change(data['1Y'])}" | |
| ) | |
| output += performance_line + '\n' | |
| # Generate ASCII chart | |
| hist_chart = stock.history(period="1mo") # last 1 month data | |
| prices = hist_chart['Close'].tolist() | |
| if len(prices) >= 2: | |
| chart = generate_sparkline(prices) | |
| # Determine the color based on last month's percentage change | |
| if data['1M'] > 0: | |
| chart_color = Fore.LIGHTGREEN_EX | |
| elif data['1M'] < 0: | |
| chart_color = Fore.RED | |
| else: | |
| chart_color = Fore.CYAN | |
| output += Fore.WHITE + f" Price Chart (Last 1 Month):\n" | |
| output += f" {chart_color}{chart}{Fore.RESET}\n" | |
| else: | |
| output += Fore.RED + " Not enough data to generate chart.\n" | |
| output += "\n" | |
| return output | |
| def display_company_data(company_name, ticker): | |
| output = '' | |
| stock = yf.Ticker(ticker) | |
| hist = stock.history(period="1y") | |
| data = { | |
| '1D': get_percentage_change(hist, days=1), | |
| '5D': get_percentage_change(hist, days=5), | |
| '1M': get_percentage_change(hist, days=30), | |
| '1Y': get_percentage_change(hist, days=365) | |
| } | |
| # Company name in light yellow | |
| output += f"\nMarket data for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', company_name.capitalize())} ({ticker}):{Fore.RESET}\n\n" | |
| # Current price | |
| current_price = stock.info.get('regularMarketPrice', 'N/A') | |
| if current_price != 'N/A': | |
| current_price = f"${current_price:.2f}" | |
| # Performance data | |
| performance_line = ( | |
| f" {Fore.LIGHTCYAN_EX}Current Price:{Fore.RESET} {Fore.LIGHTGREEN_EX}{current_price}{Fore.RESET}\n" + | |
| Fore.WHITE + f" 1D: {format_percentage_change(data['1D'])} | " + | |
| Fore.WHITE + f"5D: {format_percentage_change(data['5D'])} | " + | |
| Fore.WHITE + f"1M: {format_percentage_change(data['1M'])} | " + | |
| Fore.WHITE + f"1Y: {format_percentage_change(data['1Y'])}" | |
| ) | |
| output += performance_line + '\n' | |
| # Generate ASCII chart | |
| hist_chart = stock.history(period="1mo") # last 1 month data | |
| prices = hist_chart['Close'].tolist() | |
| if len(prices) >= 2: | |
| chart = generate_sparkline(prices) | |
| # Determine the color based on last month's percentage change | |
| if data['1M'] > 0: | |
| chart_color = Fore.LIGHTGREEN_EX | |
| elif data['1M'] < 0: | |
| chart_color = Fore.RED | |
| else: | |
| chart_color = Fore.CYAN | |
| output += Fore.WHITE + f"\n Price Chart (Last 1 Month):\n" | |
| output += f" {chart_color}{chart}{Fore.RESET}\n" | |
| else: | |
| output += Fore.RED + "\n Not enough data to generate chart.\n" | |
| # Top News | |
| news = stock.news[:5] | |
| if news: | |
| output += Fore.WHITE + "\n Top News:\n\n" | |
| for article in news: | |
| output += Fore.LIGHTBLUE_EX + f" - {article['title']}\n" | |
| output += Fore.WHITE + f" {article['link']}\n" | |
| output += '\n' | |
| else: | |
| output += Fore.RED + " No recent news articles found.\n\n" | |
| return output | |
| def display_statistics(ticker): | |
| stock = yf.Ticker(ticker) | |
| info = stock.info | |
| output = f"\nKey Statistics for {Fore.LIGHTYELLOW_EX}{info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n\n" | |
| try: | |
| # Valuation Measures | |
| output += Fore.LIGHTYELLOW_EX + "Valuation Measures:\n" + Fore.RESET | |
| valuation_keys = [ | |
| 'marketCap', 'enterpriseValue', 'trailingPE', 'forwardPE', | |
| 'priceToSalesTrailing12Months', 'priceToBook', 'enterpriseToRevenue', | |
| 'enterpriseToEbitda' | |
| ] | |
| for key in valuation_keys: | |
| value = info.get(key, 'N/A') | |
| if value != 'N/A': | |
| if key in ['marketCap', 'enterpriseValue']: | |
| value = format_currency(value) | |
| else: | |
| value = f"{value:.2f}" | |
| output += f" {Fore.LIGHTCYAN_EX}{key}:{Fore.RESET} {value}\n" | |
| output += "\n" | |
| # Financial Highlights | |
| output += Fore.LIGHTYELLOW_EX + "Financial Highlights:\n" + Fore.RESET | |
| financial_keys = [ | |
| 'ebitdaMargins', 'profitMargins', 'grossMargins', 'operatingMargins', | |
| 'returnOnAssets', 'returnOnEquity', 'revenue', 'revenuePerShare', | |
| 'quarterlyRevenueGrowth', 'grossProfits', 'ebitda', 'netIncomeToCommon' | |
| ] | |
| for key in financial_keys: | |
| value = info.get(key, 'N/A') | |
| if value != 'N/A': | |
| if 'Margins' in key or 'returnOn' in key or 'quarterlyRevenueGrowth' in key: | |
| value = format_percentage(value) | |
| elif key in ['revenue', 'revenuePerShare', 'grossProfits', 'ebitda', 'netIncomeToCommon']: | |
| value = format_currency(value) | |
| else: | |
| value = f"{value:.2f}" | |
| output += f" {Fore.LIGHTCYAN_EX}{key}:{Fore.RESET} {value}\n" | |
| except Exception as e: | |
| output += Fore.RED + " Unable to retrieve key statistics.\n" | |
| return output | |
| def display_history(ticker): | |
| stock = yf.Ticker(ticker) | |
| hist = stock.history(period="1y") | |
| output = f"\nHistorical Data for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n\n" | |
| if not hist.empty: | |
| # Select relevant columns and format index | |
| hist = hist[['Open', 'High', 'Low', 'Close', 'Volume']] | |
| hist.index = hist.index.strftime('%Y-%m-%d') | |
| output += hist.tail(10).to_string() | |
| else: | |
| output += Fore.RED + " No historical data available.\n" | |
| return output | |
| def display_profile(ticker): | |
| stock = yf.Ticker(ticker) | |
| info = stock.info | |
| output = f"\nProfile for {Fore.LIGHTYELLOW_EX}{info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n\n" | |
| output += f" {Fore.LIGHTCYAN_EX}Industry:{Fore.RESET} {info.get('industry', 'N/A')}\n" | |
| output += f" {Fore.LIGHTCYAN_EX}Sector:{Fore.RESET} {info.get('sector', 'N/A')}\n" | |
| output += f" {Fore.LIGHTCYAN_EX}Full Time Employees:{Fore.RESET} {info.get('fullTimeEmployees', 'N/A')}\n" | |
| output += f" {Fore.LIGHTCYAN_EX}Website:{Fore.RESET} {info.get('website', 'N/A')}\n" | |
| output += f"\n {Fore.LIGHTYELLOW_EX}Description:{Fore.RESET}\n\n" | |
| output += f" {info.get('longBusinessSummary', 'N/A')}\n" | |
| return output | |
| def display_financials(ticker): | |
| stock = yf.Ticker(ticker) | |
| output = f"\nFinancials for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n" | |
| # Income Statement | |
| income_stmt = stock.financials | |
| if not income_stmt.empty: | |
| output += f"\n{Fore.LIGHTYELLOW_EX}Income Statement (in thousands):{Fore.RESET}\n" | |
| income_stmt = income_stmt / 1000 # Convert to thousands for readability | |
| income_stmt = income_stmt.transpose() | |
| income_stmt.index = income_stmt.index.strftime('%Y-%m-%d') | |
| # Ensure the required columns are present | |
| income_columns = ['Total Revenue', 'Cost Of Revenue', 'Gross Profit', 'Operating Income', 'Net Income'] | |
| existing_columns = [col for col in income_columns if col in income_stmt.columns] | |
| income_stmt = income_stmt[existing_columns] | |
| output += income_stmt.to_string() | |
| else: | |
| output += Fore.RED + "\n No income statement data available.\n" | |
| # Balance Sheet | |
| balance_sheet = stock.balance_sheet | |
| if not balance_sheet.empty: | |
| output += f"\n\n{Fore.LIGHTYELLOW_EX}Balance Sheet (in thousands):{Fore.RESET}\n" | |
| balance_sheet = balance_sheet / 1000 # Convert to thousands | |
| balance_sheet = balance_sheet.transpose() | |
| balance_sheet.index = balance_sheet.index.strftime('%Y-%m-%d') | |
| # Ensure the required columns are present | |
| balance_columns = ['Total Assets', 'Total Liab', 'Total Stockholder Equity'] | |
| existing_columns = [col for col in balance_columns if col in balance_sheet.columns] | |
| balance_sheet = balance_sheet[existing_columns] | |
| output += balance_sheet.to_string() | |
| else: | |
| output += Fore.RED + "\n No balance sheet data available.\n" | |
| # Cash Flow Statement | |
| cash_flow = stock.cashflow | |
| if not cash_flow.empty: | |
| output += f"\n\n{Fore.LIGHTYELLOW_EX}Cash Flow Statement (in thousands):{Fore.RESET}\n" | |
| cash_flow = cash_flow / 1000 # Convert to thousands | |
| cash_flow = cash_flow.transpose() | |
| cash_flow.index = cash_flow.index.strftime('%Y-%m-%d') | |
| # Ensure the required columns are present | |
| cash_flow_columns = ['Total Cash From Operating Activities', 'Total Cashflows From Investing Activities', 'Total Cash From Financing Activities'] | |
| existing_columns = [col for col in cash_flow_columns if col in cash_flow.columns] | |
| cash_flow = cash_flow[existing_columns] | |
| output += cash_flow.to_string() | |
| else: | |
| output += Fore.RED + "\n No cash flow data available.\n" | |
| return output | |
| def display_analysis(ticker): | |
| stock = yf.Ticker(ticker) | |
| company_name = stock.info.get('shortName', ticker) | |
| output = f"\nAnalysis for {Fore.LIGHTYELLOW_EX}{company_name} ({ticker}):{Fore.RESET}\n" | |
| # Analyst Recommendations | |
| recommendations = stock.recommendations | |
| if recommendations is not None and not recommendations.empty: | |
| output += Fore.LIGHTYELLOW_EX + "\nRecent Analyst Recommendations:\n" + Fore.RESET | |
| # Take the last 5 recommendations and create a copy | |
| recs = recommendations.tail(5).copy() | |
| # Reset index to include 'Date' as a column | |
| recs.reset_index(inplace=True) | |
| # Format 'Date' column | |
| if 'Date' in recs.columns: | |
| recs['Date'] = recs['Date'].dt.strftime('%Y-%m-%d') | |
| else: | |
| recs.rename(columns={'index': 'Date'}, inplace=True) | |
| recs['Date'] = recs['Date'].astype(str) | |
| # Ensure required columns are present | |
| required_columns = ['Date', 'Firm', 'To Grade', 'From Grade', 'Action'] | |
| for col in required_columns: | |
| if col not in recs.columns: | |
| recs.loc[:, col] = 'N/A' | |
| # Select required columns | |
| recs_display = recs[required_columns] | |
| output += recs_display.to_string(index=False) | |
| else: | |
| output += Fore.RED + "\n No analyst recommendations available.\n" | |
| output += "\n\n" | |
| # Price Target | |
| price_target = stock.info.get('targetMeanPrice', 'N/A') | |
| number_of_analysts = stock.info.get('numberOfAnalystOpinions', 'N/A') | |
| output += Fore.LIGHTYELLOW_EX + "Analyst Price Target:\n" + Fore.RESET | |
| output += f" {Fore.LIGHTCYAN_EX}Target Mean Price:{Fore.RESET} {price_target}\n" | |
| output += f" {Fore.LIGHTCYAN_EX}Number of Analysts:{Fore.RESET} {number_of_analysts}\n" | |
| return output | |
| def display_options(ticker): | |
| stock = yf.Ticker(ticker) | |
| options_dates = stock.options | |
| output = f"\nOptions for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n\n" | |
| if options_dates: | |
| output += Fore.LIGHTYELLOW_EX + "Available Options Expiration Dates:\n" + Fore.RESET | |
| for date in options_dates: | |
| output += f" {date}\n" | |
| nearest_date = options_dates[0] | |
| options_chain = stock.option_chain(nearest_date) | |
| output += f"\nOptions Chain for {nearest_date} (Showing top 5 calls and puts):\n\n" | |
| output += Fore.LIGHTYELLOW_EX + "Calls:\n" + Fore.RESET | |
| calls = options_chain.calls.head(5) | |
| output += calls.to_string(index=False) | |
| output += "\n\n" + Fore.LIGHTYELLOW_EX + "Puts:\n" + Fore.RESET | |
| puts = options_chain.puts.head(5) | |
| output += puts.to_string(index=False) | |
| else: | |
| output += Fore.RED + " No options data available.\n" | |
| return output | |
| def display_holders(ticker): | |
| stock = yf.Ticker(ticker) | |
| major_holders = stock.major_holders | |
| institutional_holders = stock.institutional_holders | |
| output = f"\nHolders for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n\n" | |
| if major_holders is not None and not major_holders.empty: | |
| output += Fore.LIGHTYELLOW_EX + "Major Holders:\n" + Fore.RESET | |
| output += major_holders.to_string(index=False, header=False) | |
| else: | |
| output += Fore.RED + " No major holders data available.\n" | |
| output += "\n\n" | |
| if institutional_holders is not None and not institutional_holders.empty: | |
| output += Fore.LIGHTYELLOW_EX + "Top Institutional Holders:\n" + Fore.RESET | |
| output += institutional_holders.head(10).to_string(index=False) | |
| else: | |
| output += Fore.RED + " No institutional holders data available.\n" | |
| return output | |
| def display_sustainability(ticker): | |
| stock = yf.Ticker(ticker) | |
| sustainability = stock.sustainability | |
| output = f"\nSustainability for {Fore.LIGHTYELLOW_EX}{stock.info.get('shortName', ticker)} ({ticker}):{Fore.RESET}\n" | |
| if sustainability is not None and not sustainability.empty: | |
| # Reset index to turn metrics into a column | |
| sus = sustainability.reset_index() | |
| sus.columns = ['Metric', 'Value'] | |
| # Format the DataFrame for display | |
| output += "\n" + Fore.LIGHTYELLOW_EX + "Sustainability Metrics:\n" + Fore.RESET | |
| output += sus.to_string(index=False) | |
| else: | |
| output += Fore.RED + "\n No sustainability data available.\n" | |
| return output | |
| def get_percentage_change(hist, days): | |
| try: | |
| end_price = hist['Close'][-1] | |
| if len(hist) >= days: | |
| start_price = hist['Close'][-days] | |
| else: | |
| start_price = hist['Close'][0] | |
| return ((end_price - start_price) / start_price) * 100 | |
| except Exception: | |
| return 0.0 | |
| def format_percentage_change(value): | |
| if value > 0: | |
| return Fore.LIGHTGREEN_EX + f"+{value:.2f}%" + Fore.RESET | |
| elif value < 0: | |
| return Fore.RED + f"{value:.2f}%" + Fore.RESET | |
| else: | |
| return Fore.WHITE + "0.00%" + Fore.RESET | |
| def format_percentage(value): | |
| try: | |
| value = float(value) * 100 | |
| if value > 0: | |
| return Fore.LIGHTGREEN_EX + f"{value:.2f}%" + Fore.RESET | |
| elif value < 0: | |
| return Fore.RED + f"{value:.2f}%" + Fore.RESET | |
| else: | |
| return Fore.WHITE + "0.00%" + Fore.RESET | |
| except: | |
| return 'N/A' | |
| def format_currency(value): | |
| try: | |
| value = float(value) | |
| if value >= 1e12: | |
| return f"${value/1e12:.2f}T" | |
| elif value >= 1e9: | |
| return f"${value/1e9:.2f}B" | |
| elif value >= 1e6: | |
| return f"${value/1e6:.2f}M" | |
| elif value >= 1e3: | |
| return f"${value/1e3:.2f}K" | |
| else: | |
| return f"${value:.2f}" | |
| except: | |
| return 'N/A' | |
| def generate_sparkline(data): | |
| if not data: | |
| return '' | |
| # Remove NaN values from the data | |
| data = [x for x in data if x == x] # Simple NaN check: NaN is the only value that doesn't equal itself | |
| if not data: # Check again if data is empty after removing NaNs | |
| return '' | |
| min_data = min(data) | |
| max_data = max(data) | |
| data_range = max_data - min_data if max_data - min_data != 0 else 1 | |
| scaled_data = [(x - min_data) / data_range for x in data] | |
| spark_chars = '▁▂▃▄▅▆▇█' | |
| result = '' | |
| for x in scaled_data: | |
| index = int(x * (len(spark_chars) - 1)) | |
| result += spark_chars[index] | |
| return result | |
| # Function to strip ANSI color codes for width calculation | |
| def strip_ansi_codes(text): | |
| ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') | |
| return ansi_escape.sub('', text) | |
| # Utility Functions for formatting | |
| def format_percentage_no_color(value): | |
| try: | |
| value = float(value) * 100 | |
| return f"{value:.2f}%" | |
| except: | |
| return 'N/A' | |
| def format_currency_no_color(value): | |
| try: | |
| value = float(value) | |
| if value >= 1e12: | |
| return f"${value/1e12:.2f}T" | |
| elif value >= 1e9: | |
| return f"${value/1e9:.2f}B" | |
| elif value >= 1e6: | |
| return f"${value/1e6:.2f}M" | |
| elif value >= 1e3: | |
| return f"${value/1e3:.2f}K" | |
| else: | |
| return f"${value:.2f}" | |
| except: | |
| return 'N/A' | |
| def format_number(value): | |
| try: | |
| return f"{value:,.0f}" | |
| except: | |
| return 'N/A' | |
| def display_currency_data(keyword, currency_code): | |
| try: | |
| currency_pair = f"{currency_code}=X" | |
| currency = yf.Ticker(currency_pair) | |
| hist = currency.history(period="1y") | |
| data = { | |
| '1D': get_percentage_change(hist, days=1), | |
| '5D': get_percentage_change(hist, days=5), | |
| '1M': get_percentage_change(hist, days=30), | |
| '1Y': get_percentage_change(hist, days=365) | |
| } | |
| output = f"\nMarket data for {Fore.LIGHTYELLOW_EX}{currency_code} (Currency):{Fore.RESET}\n\n" | |
| current_price = currency.info.get('regularMarketPrice', 'N/A') | |
| if current_price != 'N/A': | |
| current_price = f"${current_price:,.2f}" | |
| performance_line = ( | |
| f"{Fore.LIGHTCYAN_EX} Current Price:{Fore.RESET} {Fore.LIGHTGREEN_EX}{current_price}{Fore.RESET}\n" + | |
| Fore.WHITE + f" 1D: {format_percentage_change(data['1D'])} | " + | |
| f"5D: {format_percentage_change(data['5D'])} | " + | |
| f"1M: {format_percentage_change(data['1M'])} | " + | |
| f"1Y: {format_percentage_change(data['1Y'])}" | |
| ) | |
| output += performance_line + "\n" | |
| hist_chart = currency.history(period="1mo") | |
| prices = hist_chart['Close'].tolist() | |
| if len(prices) >= 2: | |
| chart = generate_sparkline(prices) | |
| chart_color = Fore.LIGHTGREEN_EX if data['1M'] > 0 else Fore.RED if data['1M'] < 0 else Fore.CYAN | |
| output += Fore.WHITE + " Price Chart (Last 1 Month):\n" | |
| output += f" {chart_color}{chart}{Fore.RESET}\n" | |
| else: | |
| output += Fore.RED + " Not enough data to generate chart.\n" | |
| output += Fore.WHITE + "\nAdditional Data:\n\n" | |
| output += f"{Fore.LIGHTCYAN_EX} 52 Week Range:{Fore.RESET} {currency.info.get('fiftyTwoWeekLow', 'N/A')} - {currency.info.get('fiftyTwoWeekHigh', 'N/A')}\n" | |
| output += f"{Fore.LIGHTCYAN_EX} Volume (24hr):{Fore.RESET} {format_number(currency.info.get('volume', 'N/A'))} (units)\n" | |
| output += f"{Fore.LIGHTCYAN_EX} Market Cap:{Fore.RESET} {format_number(currency.info.get('marketCap', 'N/A'))} USD\n" | |
| return output | |
| except Exception as e: | |
| return Fore.RED + f"Error fetching data for {currency_code}: {str(e)}" | |
| def display_crypto_data(keyword, crypto_code): | |
| try: | |
| crypto = yf.Ticker(crypto_code) | |
| hist = crypto.history(period="1y") | |
| data = { | |
| '1D': get_percentage_change(hist, days=1), | |
| '5D': get_percentage_change(hist, days=5), | |
| '1M': get_percentage_change(hist, days=30), | |
| '1Y': get_percentage_change(hist, days=365) | |
| } | |
| crypto_name = crypto_code.split('-')[0].upper() | |
| output = f"\nMarket data for {Fore.LIGHTYELLOW_EX}{crypto_name} (Crypto):{Fore.RESET}\n\n" | |
| current_price = crypto.info.get('regularMarketPrice', 'N/A') | |
| if current_price != 'N/A': | |
| current_price = f"${current_price:,.2f}" | |
| performance_line = ( | |
| f"{Fore.LIGHTCYAN_EX} Current Price:{Fore.RESET} {Fore.LIGHTGREEN_EX}{current_price}{Fore.RESET}\n" + | |
| Fore.WHITE + f" 1D: {format_percentage_change(data['1D'])} | " + | |
| f"5D: {format_percentage_change(data['5D'])} | " + | |
| f"1M: {format_percentage_change(data['1M'])} | " + | |
| f"1Y: {format_percentage_change(data['1Y'])}" | |
| ) | |
| output += performance_line + "\n" | |
| hist_chart = crypto.history(period="1mo") | |
| prices = hist_chart['Close'].tolist() | |
| if len(prices) >= 2: | |
| chart = generate_sparkline(prices) | |
| chart_color = Fore.LIGHTGREEN_EX if data['1M'] > 0 else Fore.RED if data['1M'] < 0 else Fore.CYAN | |
| output += Fore.WHITE + " Price Chart (Last 1 Month):\n" | |
| output += f" {chart_color}{chart}{Fore.RESET}\n" | |
| else: | |
| output += Fore.RED + " Not enough data to generate chart.\n" | |
| output += Fore.WHITE + "\nAdditional Data:\n\n" | |
| output += f"{Fore.LIGHTCYAN_EX} 52 Week Range:{Fore.RESET} {crypto.info.get('fiftyTwoWeekLow', 'N/A')} - {crypto.info.get('fiftyTwoWeekHigh', 'N/A')}\n" | |
| output += f"{Fore.LIGHTCYAN_EX} Volume (24hr):{Fore.RESET} {format_number(crypto.info.get('volume', 'N/A'))} USD\n" | |
| output += f"{Fore.LIGHTCYAN_EX} Market Cap:{Fore.RESET} {format_number(crypto.info.get('marketCap', 'N/A'))} USD\n" | |
| output += f"{Fore.LIGHTCYAN_EX} Circulating Supply:{Fore.RESET} {format_number(crypto.info.get('circulatingSupply', 'N/A'))} coins\n" | |
| return output | |
| except Exception as e: | |
| return Fore.RED + f"Error fetching data for {crypto_code}: {str(e)}" | |
| # Fetch stock data with error handling | |
| def fetch_stock_data(ticker): | |
| try: | |
| stock = yf.Ticker(ticker) | |
| if not stock.info or stock.info == {}: | |
| raise ValueError(Fore.RED + f"No data available for {ticker}") | |
| return stock | |
| except Exception as e: | |
| print(f"{Fore.RED}Error fetching data for {ticker}: {e}{Fore.RESET}") | |
| return None | |
| # Main function to compare stocks | |
| def stock_compare(stock1_input, stock2_input): | |
| # First, normalize the inputs to tickers using the keyword_mapping dictionary | |
| stock1_ticker = keyword_mapping['companies'].get(stock1_input.lower(), stock1_input.upper()) | |
| stock2_ticker = keyword_mapping['companies'].get(stock2_input.lower(), stock2_input.upper()) | |
| # Fetch data for both stocks | |
| stock1 = fetch_stock_data(stock1_ticker) | |
| if not stock1: | |
| return Fore.RED + f"No data available for {stock1_ticker}." | |
| time.sleep(2) # Adding delay to avoid throttling | |
| stock2 = fetch_stock_data(stock2_ticker) | |
| if not stock2: | |
| return Fore.RED + f"No data available for {stock2_ticker}." | |
| # Get company names from the stock data or fallback to ticker | |
| company_name1 = stock1.info.get('shortName', stock1_ticker) | |
| company_name2 = stock2.info.get('shortName', stock2_ticker) | |
| # Metrics to compare | |
| metrics = { | |
| 'Market Cap': ('marketCap', format_currency_no_color), | |
| 'Total Revenue': ('totalRevenue', format_currency_no_color), | |
| 'Revenue Growth': ('revenueGrowth', format_percentage_no_color), | |
| 'Gross Profit Margin': ('grossMargins', format_percentage_no_color), | |
| 'Operating Margin': ('operatingMargins', format_percentage_no_color), | |
| 'Net Income': ('netIncomeToCommon', format_currency_no_color), | |
| 'EPS (TTM)': ('trailingEps', lambda x: f"{x:.2f}" if x is not None else 'N/A'), | |
| 'P/E Ratio (TTM)': ('trailingPE', lambda x: f"{x:.2f}" if x is not None else 'N/A'), | |
| 'Return on Equity': ('returnOnEquity', format_percentage_no_color), | |
| 'Debt to Equity Ratio': (None, None) # Will calculate manually if needed | |
| } | |
| # Collect data | |
| data = {} | |
| for metric, (key, formatter) in metrics.items(): | |
| if key: | |
| value1 = stock1.info.get(key, None) | |
| value2 = stock2.info.get(key, None) | |
| formatted_value1 = formatter(value1) if value1 is not None else 'N/A' | |
| formatted_value2 = formatter(value2) if value2 is not None else 'N/A' | |
| else: | |
| # Debt to Equity Ratio | |
| total_debt1 = stock1.info.get('totalDebt', None) | |
| equity1 = stock1.info.get('totalStockholderEquity', None) | |
| ratio1 = total_debt1 / equity1 if total_debt1 and equity1 else None | |
| total_debt2 = stock2.info.get('totalDebt', None) | |
| equity2 = stock2.info.get('totalStockholderEquity', None) | |
| ratio2 = total_debt2 / equity2 if total_debt2 and equity2 else None | |
| formatted_value1 = f"{ratio1:.2f}" if ratio1 is not None else 'N/A' | |
| formatted_value2 = f"{ratio2:.2f}" if ratio2 is not None else 'N/A' | |
| # Apply color coding for comparison | |
| try: | |
| value1_num = float(strip_ansi_codes(formatted_value1).replace('%', '').replace('$', '').replace('B', '')) | |
| value2_num = float(strip_ansi_codes(formatted_value2).replace('%', '').replace('$', '').replace('B', '')) | |
| if value1_num > value2_num: | |
| formatted_value1 = Fore.LIGHTGREEN_EX + formatted_value1 + Fore.RESET | |
| formatted_value2 = Fore.RED + formatted_value2 + Fore.RESET | |
| elif value1_num < value2_num: | |
| formatted_value1 = Fore.RED + formatted_value1 + Fore.RESET | |
| formatted_value2 = Fore.LIGHTGREEN_EX + formatted_value2 + Fore.RESET | |
| except ValueError: | |
| pass # In case value is not numeric (e.g., 'N/A') | |
| data[metric] = [formatted_value1, formatted_value2] | |
| # Fixed-width column setup | |
| metric_width = 25 | |
| company1_width = max(20, len(company_name1) + 5) | |
| company2_width = max(20, len(company_name2) + 5) | |
| # Header | |
| output = f"\nComparison between {Fore.LIGHTYELLOW_EX}{company_name1} ({stock1_ticker}){Fore.RESET} and {Fore.LIGHTYELLOW_EX}{company_name2} ({stock2_ticker}):\n" | |
| output += f"{'Metric':<{metric_width}} {company_name1:<{company1_width}} {company_name2:<{company2_width}}\n" | |
| output += "-" * (metric_width + company1_width + company2_width) + "\n" | |
| # Rows with proper padding | |
| for metric, values in data.items(): | |
| metric_name = metric.ljust(metric_width) | |
| company1_value = strip_ansi_codes(values[0]).ljust(company1_width) | |
| company2_value = strip_ansi_codes(values[1]).ljust(company2_width) | |
| # Apply color after padding | |
| colored_value1 = values[0].replace(strip_ansi_codes(values[0]), company1_value) | |
| colored_value2 = values[1].replace(strip_ansi_codes(values[1]), company2_value) | |
| output += f"{metric_name} {colored_value1} {colored_value2}\n" | |
| return output | |
| # Add these new functions after the existing functions but before handle_markets_command | |
| def generate_oil_market_report(): | |
| # Get Athens time | |
| athens_time = datetime.now(timezone('Europe/Athens')).strftime("%Y-%m-%d %H:%M:%S Athens") | |
| output = f""" | |
| {Fore.MAGENTA}╔═════════════════════════════════════════════════════════════╗ | |
| ║ {Fore.CYAN}█▀▀ █ █▀█ █▄▄ ▄▀█ █ █▀█ █ █ {Fore.MAGENTA}& {Fore.CYAN}█▀▀ ▄▀█ █▀{Fore.MAGENTA} ║ | |
| ║ {Fore.CYAN}█▄█ █▄▄ █▄█ █▄█ █▀█ █▄▄ █▄█ █ █▄▄ {Fore.MAGENTA}& {Fore.CYAN}█▄█ █▀█ ▄█{Fore.MAGENTA} ║ | |
| ║ MARKET INTELLIGENCE SYSTEM ║ | |
| ║ By ARPA HELLENIC LOGICAL SYSTEMS ║ | |
| ║ {athens_time} ║ | |
| ╚══════════════════════════════════════════════════════════════╝{Fore.RESET} | |
| """ | |
| try: | |
| output += generate_market_overview() | |
| output += predict_oil_prices() | |
| output += analyze_industry_news() | |
| output += analyze_top_players() | |
| output += analyze_global_supply() | |
| output += analyze_industry_trends() | |
| except Exception as e: | |
| output += f"\n{Fore.RED}Error generating market report: {str(e)}{Fore.RESET}" | |
| return output | |
| def predict_oil_prices(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ OIL PRICE PREDICTION{Fore.RESET}\n" | |
| try: | |
| wti = yf.Ticker("CL=F") | |
| hist = wti.history(period="1y") | |
| if hist.empty: | |
| output += f"{Fore.RED}Not enough data to make predictions.{Fore.RESET}\n" | |
| return output | |
| prices = hist['Close'].dropna().values | |
| if len(prices) < 30: | |
| return f"{Fore.RED}Insufficient data for prediction.{Fore.RESET}\n" | |
| log_prices = np.log(prices) | |
| model = sm.tsa.ARIMA(log_prices, order=(5,1,0)) | |
| model_fit = model.fit() | |
| # Get forecast values as a numpy array | |
| forecast = model_fit.forecast(steps=7) | |
| forecast_values = np.exp(forecast) | |
| # Convert current price to scalar | |
| current_price = prices[-1] | |
| output += f"\n{Fore.CYAN}Current WTI Price: ${current_price:.2f}{Fore.RESET}\n" | |
| output += f"{Fore.CYAN}7-Day Price Forecast:{Fore.RESET}\n" | |
| # Generate dates | |
| last_date = hist.index[-1] | |
| forecast_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=7) | |
| # Process each forecast value individually | |
| for i, date in enumerate(forecast_dates): | |
| price = forecast_values[i] # Get single value from array | |
| change = ((price - current_price) / current_price) * 100 | |
| change_color = Fore.LIGHTGREEN_EX if price > current_price else Fore.RED | |
| output += f"{date.strftime('%Y-%m-%d')}: ${price:.2f} ({change_color}{change:+.2f}%{Fore.RESET})\n" | |
| # Generate trend visualization | |
| sparkline = generate_sparkline(forecast_values) | |
| # Compare first and last values directly | |
| trend_up = forecast_values[-1] > forecast_values[0] | |
| trend_color = Fore.LIGHTGREEN_EX if trend_up else Fore.RED | |
| output += f"\n{Fore.CYAN}Forecasted Price Trend:{Fore.RESET}\n" | |
| output += f" {trend_color}{sparkline}{Fore.RESET} " | |
| output += f"[Range: ${np.min(forecast_values):.2f} - ${np.max(forecast_values):.2f}]\n" | |
| # Calculate trend percentage using scalar values | |
| trend_pct = ((forecast_values[-1] - forecast_values[0]) / forecast_values[0]) * 100 | |
| if trend_up: # Use the boolean we already calculated | |
| output += f" Upward trend expected: {Fore.LIGHTGREEN_EX}+{trend_pct:.1f}%{Fore.RESET} over 7 days\n" | |
| else: | |
| output += f" Downward trend expected: {Fore.RED}{trend_pct:.1f}%{Fore.RESET} over 7 days\n" | |
| except Exception as e: | |
| output += f"{Fore.RED}Error making price prediction: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def analyze_industry_news(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ INDUSTRY NEWS ANALYSIS{Fore.RESET}\n" | |
| try: | |
| # Use multiple news sources and APIs | |
| news_sources = [ | |
| {'ticker': 'CL=F', 'type': 'Oil Futures'}, | |
| {'ticker': 'XLE', 'type': 'Energy Sector'}, | |
| {'ticker': 'XOM', 'type': 'Oil & Gas'}, | |
| {'ticker': 'CVX', 'type': 'Oil & Gas'} | |
| ] | |
| all_news = [] | |
| for source in news_sources: | |
| ticker_obj = yf.Ticker(source['ticker']) | |
| news = ticker_obj.news | |
| for article in news: | |
| # Extract the actual source from the article URL | |
| domain = article['link'].split('/')[2] | |
| if 'yahoo' in domain: | |
| actual_source = 'Yahoo Finance' | |
| elif 'reuters' in domain: | |
| actual_source = 'Reuters' | |
| elif 'bloomberg' in domain: | |
| actual_source = 'Bloomberg' | |
| elif 'ft.com' in domain: | |
| actual_source = 'Financial Times' | |
| else: | |
| actual_source = domain.replace('www.', '').capitalize() | |
| article['source'] = actual_source | |
| article['sector'] = source['type'] | |
| all_news.append(article) | |
| # Sort by publication date | |
| all_news.sort(key=lambda x: x.get('providerPublishTime', 0), reverse=True) | |
| # Take unique articles | |
| seen_titles = set() | |
| unique_news = [] | |
| for article in all_news: | |
| if article['title'] not in seen_titles: | |
| seen_titles.add(article['title']) | |
| unique_news.append(article) | |
| output += f"\n{Fore.WHITE}Latest Market Intelligence:{Fore.RESET}\n" | |
| # Display top 5 unique news with enhanced analysis | |
| for article in unique_news[:5]: | |
| title = article['title'] | |
| source = article['source'] | |
| sector = article['sector'] | |
| link = article['link'] | |
| output += f"\n{Fore.LIGHTBLUE_EX}{title}{Fore.RESET}\n" | |
| output += f"Source: {source} | Sector: {sector}\n" | |
| output += f"{link}\n" | |
| output += f"Analysis: {analyze_sentiment(title)}\n" | |
| # Overall market sentiment | |
| positive = sum(1 for a in unique_news[:5] if 'Positive' in analyze_sentiment(a['title'])) | |
| negative = sum(1 for a in unique_news[:5] if 'Negative' in analyze_sentiment(a['title'])) | |
| output += f"\n{Fore.WHITE}Market Sentiment Summary:{Fore.RESET} " | |
| if positive > negative: | |
| output += f"{Fore.LIGHTGREEN_EX}Predominantly Positive{Fore.RESET}\n" | |
| elif negative > positive: | |
| output += f"{Fore.RED}Predominantly Negative{Fore.RESET}\n" | |
| else: | |
| output += f"{Fore.YELLOW}Mixed/Neutral{Fore.RESET}\n" | |
| except Exception as e: | |
| output += f"{Fore.RED}Error fetching or analyzing news: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def analyze_sentiment(text): | |
| # Simple sentiment analysis based on keywords | |
| positive_keywords = ['gain', 'rise', 'up', 'increase', 'positive', 'surge', 'growth', 'profit'] | |
| negative_keywords = ['fall', 'drop', 'down', 'decrease', 'negative', 'decline', 'loss', 'plunge'] | |
| text = text.lower() | |
| positive_score = sum([text.count(word) for word in positive_keywords]) | |
| negative_score = sum([text.count(word) for word in negative_keywords]) | |
| if positive_score > negative_score: | |
| return f"{Fore.LIGHTGREEN_EX}Positive{Fore.RESET}" | |
| elif negative_score > positive_score: | |
| return f"{Fore.RED}Negative{Fore.RESET}" | |
| else: | |
| return f"{Fore.YELLOW}Neutral{Fore.RESET}" | |
| def generate_market_overview(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ MARKET OVERVIEW{Fore.RESET}\n" | |
| try: | |
| # Get data for each commodity | |
| commodities = { | |
| 'WTI': yf.Ticker("CL=F"), | |
| 'Brent': yf.Ticker("BZ=F"), | |
| 'Gas': yf.Ticker("NG=F") | |
| } | |
| output += f"\n{Fore.CYAN}Price Trends (30-Day):{Fore.RESET}\n" | |
| for name, ticker in commodities.items(): | |
| hist = ticker.history(period="1mo") | |
| if not hist.empty: | |
| prices = hist['Close'] | |
| change_30d = ((prices[-1] - prices[0]) / prices[0]) * 100 | |
| sparkline = generate_sparkline(prices.tolist()) | |
| # Color based on performance | |
| chart_color = Fore.LIGHTGREEN_EX if change_30d >= 0 else Fore.RED | |
| # Format the line with price range and percentage | |
| output += f"{name:<6} {chart_color}{sparkline}{Fore.RESET} " | |
| output += f"[${min(prices):.2f} - ${max(prices):.2f}] " | |
| output += f"({chart_color}{change_30d:+.2f}%{Fore.RESET})\n" | |
| else: | |
| output += f"{name:<6} {Fore.RED}No data available{Fore.RESET}\n" | |
| except Exception as e: | |
| output += f"{Fore.RED}Error generating market overview: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def analyze_top_players(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ TOP PLAYERS ANALYSIS{Fore.RESET}\n" | |
| try: | |
| companies = [ | |
| ('XOM', 'ExxonMobil'), ('CVX', 'Chevron'), | |
| ('SHEL', 'Shell'), ('TTE', 'TotalEnergies'), | |
| ('BP', 'BP'), ('COP', 'ConocoPhillips') | |
| ] | |
| # Financial metrics table (keep as is) | |
| output += "\n╔════════════════╦══════════════╦══════════════╦═══════════╦═══════╗\n" | |
| output += "║ Company ║ Market Cap ║ Revenue ║ Margin ║ P/E ║\n" | |
| output += "╠════════════════╬══════════════╬══════════════╬═══════════╬═══════╣\n" | |
| for ticker, name in companies: | |
| try: | |
| stock = yf.Ticker(ticker) | |
| info = stock.info | |
| market_cap = format_currency(info.get('marketCap', 'N/A')) | |
| revenue = format_currency(info.get('totalRevenue', 'N/A')) | |
| margin = format_percentage(info.get('operatingMargins', 'N/A')) | |
| pe = f"{info.get('trailingPE', 'N/A'):.2f}" if info.get('trailingPE') else 'N/A' | |
| output += f"║ {name:<14} ║ {market_cap:<12} ║ {revenue:<12} ║ {margin:<9} ║ {pe:<6} ║\n" | |
| except: | |
| output += f"║ {name:<14} ║ {'N/A':<12} ║ {'N/A':<12} ║ {'N/A':<9} ║ {'N/A':<6} ║\n" | |
| output += "╚════════════════╩══════════════╩══════════════╩═══════════╩═══════╝\n" | |
| # Performance Comparison section with fixed formatting | |
| output += f"\n{Fore.WHITE}Performance Comparison (YTD):{Fore.RESET}\n" | |
| for ticker, name in companies: | |
| try: | |
| stock = yf.Ticker(ticker) | |
| hist = stock.history(period="ytd") | |
| # Calculate YTD change | |
| ytd_change = ((hist['Close'][-1] - hist['Close'][0]) / hist['Close'][0]) * 100 | |
| # Get 30-point sample for sparkline | |
| prices = hist['Close'].tolist() | |
| sample_size = min(30, len(prices)) | |
| sampled_prices = prices[::len(prices)//sample_size][:sample_size] | |
| # Generate sparkline and determine color | |
| chart = generate_sparkline(sampled_prices) | |
| chart_color = Fore.LIGHTGREEN_EX if ytd_change >= 0 else Fore.RED | |
| # Format the line with fixed spacing | |
| output += f"{name:<15} " | |
| output += f"{chart_color}{chart}{Fore.RESET} " | |
| output += f"[${min(prices):.2f} - ${max(prices):.2f}] " | |
| output += f"({chart_color}{ytd_change:+.2f}%{Fore.RESET})\n" | |
| except Exception as e: | |
| continue | |
| except Exception as e: | |
| output += f"{Fore.RED}Error analyzing top players: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def analyze_global_supply(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ GLOBAL SUPPLY & DEMAND ANALYSIS{Fore.RESET}\n" | |
| try: | |
| oil_etfs = ['USO', 'BNO', 'OIL', 'DBO'] | |
| output += f"\n{Fore.WHITE}Market ETF Performance:{Fore.RESET}\n" | |
| for etf in oil_etfs: | |
| try: | |
| fund = yf.Ticker(etf) | |
| hist = fund.history(period="1mo") | |
| # Calculate monthly change | |
| monthly_change = ((hist['Close'][-1] - hist['Close'][0]) / hist['Close'][0]) * 100 | |
| # Generate colored sparkline | |
| chart = generate_sparkline(hist['Close'].tolist()) | |
| chart_color = Fore.LIGHTGREEN_EX if monthly_change >= 0 else Fore.RED | |
| # Add price range and percentage change | |
| output += f"{etf:<6} " | |
| output += f"{chart_color}{chart}{Fore.RESET} " | |
| output += f"[${min(hist['Close']):.2f} - ${max(hist['Close']):.2f}] " | |
| output += f"({chart_color}{monthly_change:+.2f}%{Fore.RESET})\n" | |
| except: | |
| continue | |
| wti = yf.Ticker("CL=F") | |
| hist = wti.history(period="1mo") | |
| avg_volume = hist['Volume'].mean() | |
| current_volume = hist['Volume'][-1] | |
| volume_ratio = current_volume / avg_volume if avg_volume else 0 | |
| output += f"\n{Fore.WHITE}Trading Volume Analysis:{Fore.RESET}\n" | |
| output += f"Average Daily Volume: {format_number(avg_volume)} barrels/day\n" | |
| output += f"Current Volume: {format_number(current_volume)} barrels/day\n" | |
| output += f"Volume Ratio: {volume_ratio:.2f}x average " | |
| # Add volume interpretation | |
| if volume_ratio > 1.2: | |
| output += f"{Fore.GREEN}(High trading activity){Fore.RESET}\n" | |
| elif volume_ratio < 0.8: | |
| output += f"{Fore.YELLOW}(Low trading activity){Fore.RESET}\n" | |
| else: | |
| output += f"{Fore.WHITE}(Normal trading activity){Fore.RESET}\n" | |
| except Exception as e: | |
| output += f"{Fore.RED}Error analyzing global supply: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def analyze_industry_trends(): | |
| output = f"\n{Fore.LIGHTMAGENTA_EX}■ INDUSTRY TRENDS & RISK ANALYSIS{Fore.RESET}\n\n" | |
| try: | |
| energy_etf = yf.Ticker("XLE") | |
| hist = energy_etf.history(period="1mo") | |
| output += f"{Fore.WHITE}Risk Indicators:{Fore.RESET}\n" | |
| output += f"Volatility: {generate_risk_meter(calculate_volatility(hist))}\n" | |
| output += f"Volume Trend: {generate_risk_meter(calculate_volume_trend(hist))}\n" | |
| output += f"Price Trend: {generate_risk_meter(calculate_price_trend(hist))}\n" | |
| except Exception as e: | |
| output += f"{Fore.RED}Error analyzing industry trends: {str(e)}{Fore.RESET}\n" | |
| return output | |
| def calculate_volatility(hist): | |
| try: | |
| returns = hist['Close'].pct_change() | |
| return min(returns.std() * np.sqrt(252), 1.0) # Annualized volatility, capped at 1.0 | |
| except: | |
| return 0.5 | |
| def calculate_volume_trend(hist): | |
| try: | |
| current_vol = hist['Volume'][-5:].mean() | |
| past_vol = hist['Volume'][:-5].mean() | |
| return min(max(current_vol / past_vol - 0.5, 0), 1) | |
| except: | |
| return 0.5 | |
| def calculate_price_trend(hist): | |
| try: | |
| prices = hist['Close'] | |
| return min(max((prices[-1] / prices[0] - 0.9) / 0.2, 0), 1) | |
| except: | |
| return 0.5 | |
| def generate_risk_meter(value): | |
| meter_length = 10 | |
| filled = int(value * meter_length) | |
| if value < 0.3: | |
| color = Fore.GREEN | |
| level = "Low" | |
| elif value < 0.7: | |
| color = Fore.YELLOW | |
| level = "Medium" | |
| else: | |
| color = Fore.RED | |
| level = "High" | |
| return f"{color}{'█' * filled}{'░' * (meter_length - filled)}{Fore.RESET} {level}" | |
| def generate_market_sentiment(): | |
| try: | |
| # Get WTI data for sentiment analysis | |
| wti = yf.Ticker("CL=F") | |
| hist = wti.history(period="5d") | |
| # Calculate basic technical indicators using proper indexing | |
| volatility = hist['Close'].std() | |
| momentum = (hist['Close'].values[-1] - hist['Close'].values[0]) / hist['Close'].values[0] | |
| # Calculate volume trend using values instead of iloc | |
| first_volume = hist['Volume'].values[0] | |
| last_volume = hist['Volume'].values[-1] | |
| volume_trend = (last_volume - first_volume) / first_volume if first_volume else 0 | |
| # Determine sentiment based on multiple factors | |
| if momentum > 0.02 and volume_trend > 0: | |
| return f"{Fore.LIGHTGREEN_EX}Bullish{Fore.RESET}" | |
| elif momentum < -0.02 and volume_trend < 0: | |
| return f"{Fore.RED}Bearish{Fore.RESET}" | |
| elif volatility > hist['Close'].mean() * 0.02: | |
| return f"{Fore.YELLOW}Volatile{Fore.RESET}" | |
| else: | |
| return f"{Fore.LIGHTCYAN_EX}Neutral{Fore.RESET}" | |
| except Exception as e: | |
| return f"{Fore.YELLOW}Neutral{Fore.RESET}" | |
| #Example usage | |
| #if __name__ == "__main__": | |
| # command = input("Enter command: ") | |
| # print(handle_markets_command(command)) |