import streamlit as st import sqlite3 import hashlib import threading import pygame from yfinance import Ticker import pandas as pd import plotly.express as px import requests import os from pathlib import Path import base64 from datetime import datetime import time # Constants INITIAL_BALANCE = 10000 UPDATE_INTERVAL = 60 DEFAULT_CURRENCY = 'USD' STOCK_LIST = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "NVDA", "BRK-B", "V", "JPM"] TIME_RANGES = { '1 Day': '1d', '5 Days': '5d', '1 Month': '1mo', '3 Months': '3mo', '6 Months': '6mo', '1 Year': '1y', '5 Years': '5y', '10 Years': '10y', 'Max': 'max' } CURRENCIES = ["USD", "EUR", "GBP", "JPY", "AUD"] # Initialize pygame for music def initialize_music(): if 'music_initialized' not in st.session_state: try: pygame.mixer.init() pygame.mixer.music.load('trade.mp3') pygame.mixer.music.play(-1) st.session_state['music_initialized'] = True except pygame.error as e: st.error(f"Failed to load or play the music: {str(e)}. Please check that 'trade.mp3' exists in the correct directory.") # Database setup def setup_database(): conn = sqlite3.connect('trading_game.db', check_same_thread=False) cursor = conn.cursor() # Create tables if they don't exist cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( username TEXT PRIMARY KEY, password TEXT, balance REAL, initial_balance REAL, currency TEXT DEFAULT 'USD' ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS portfolios ( username TEXT, ticker TEXT, shares REAL, initial_investment REAL, PRIMARY KEY (username, ticker), FOREIGN KEY (username) REFERENCES users (username) ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS financial_logs ( username TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, total_value REAL, FOREIGN KEY (username) REFERENCES users (username) ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS transactions ( username TEXT, action TEXT, ticker TEXT, amount REAL, price REAL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() return conn # Ensure Database Schema def ensure_database_schema(): conn = sqlite3.connect('trading_game.db') c = conn.cursor() # Ensure the users table exists and has the required columns c.execute(''' CREATE TABLE IF NOT EXISTS users ( username TEXT PRIMARY KEY, password TEXT, balance REAL, initial_balance REAL, currency TEXT DEFAULT 'USD' ) ''') # Check if the initial_balance column exists, if not, add it c.execute("PRAGMA table_info(users)") columns = [column[1] for column in c.fetchall()] if 'initial_balance' not in columns: c.execute(f"ALTER TABLE users ADD COLUMN initial_balance REAL DEFAULT {INITIAL_BALANCE}") conn.commit() # Ensure the portfolios table exists and has the required columns c.execute(''' CREATE TABLE IF NOT EXISTS portfolios ( username TEXT, ticker TEXT, shares REAL, initial_investment REAL, PRIMARY KEY (username, ticker), FOREIGN KEY (username) REFERENCES users (username) ) ''') # Check if the initial_investment column exists, if not, add it c.execute("PRAGMA table_info(portfolios)") columns = [column[1] for column in c.fetchall()] if 'initial_investment' not in columns: c.execute(f"ALTER TABLE portfolios ADD COLUMN initial_investment REAL") conn.commit() conn.close() # Function to hash passwords def hash_password(password): return hashlib.sha256(password.encode()).hexdigest() # Function to set the background image def set_background_image(image_path): if Path(image_path).exists(): try: with open(image_path, "rb") as img_file: encoded_img = base64.b64encode(img_file.read()).decode('utf-8') st.markdown( f""" """, unsafe_allow_html=True ) except Exception as e: st.error(f"Error loading image: {str(e)}") else: st.error('Image not found. Please check the file name and path.') # Function to fetch the current price of stocks or currencies def get_stock_data(ticker, period='1d'): try: stock = Ticker(ticker) df = stock.history(period=period) if not df.empty: df.reset_index(inplace=True) return df else: return None except Exception as e: print(f"Error fetching data: {str(e)}") return None # Function to convert currency def convert_currency(amount, from_currency, to_currency): if from_currency == to_currency: return amount try: response = requests.get(f'https://api.exchangerate-api.com/v4/latest/{from_currency}') rates = response.json()['rates'] conversion_rate = rates.get(to_currency, 1) return amount * conversion_rate except Exception as e: print(f"Error converting currency: {str(e)}") return amount # Function to get market status def get_market_status(): market_status = {} for ticker in STOCK_LIST: df = get_stock_data(ticker, period='1d') if df is not None: open_price = df['Open'].iloc[0] current_price = df['Close'].iloc[-1] change = (current_price - open_price) / open_price * 100 market_status[ticker] = change return market_status # Function to fetch and plot historical data for holdings def plot_holdings(holdings, time_range): fig = px.line(title=f"Holdings - {time_range}") for ticker in holdings: df = get_stock_data(ticker, period=TIME_RANGES[time_range]) if df is not None and not df.empty: initial_investment = holdings[ticker]['initial_investment'] fig.add_scatter(x=df['Date'], y=df['Close'], mode='lines', name=f"{ticker} (Invested: ${initial_investment:.2f})") st.plotly_chart(fig, use_container_width=True) # Function to display the market overview ticker def market_overview_ticker(market_status): ticker_text = ''.join([ f" 0 else 'red'};'>{ticker}: {change:.2f}% | " for ticker, change in market_status.items() ]) st.markdown(f"""
{ticker_text}
""", unsafe_allow_html=True) # Function to fetch currency values def get_currency_values(): currencies = ['EUR', 'GBP', 'JPY', 'AUD'] currency_data = {} for currency in currencies: rate = convert_currency(1, 'USD', currency) currency_data[currency] = rate return currency_data # Function to fetch gold price from yfinance def get_gold_price(): gold_ticker = Ticker("GC=F") df = gold_ticker.history(period="1d") if not df.empty: return df['Close'].iloc[-1] else: st.error("Failed to fetch the goldprice.") return None # Function to display currency and gold values def display_currency_and_gold(): st.markdown('
Currency and Gold Values
', unsafe_allow_html=True) currency_data = get_currency_values() gold_price = get_gold_price() for currency, value in currency_data.items(): st.markdown(f'
{currency}/USD: {value:.2f}
', unsafe_allow_html=True) if gold_price: st.markdown(f'
Gold/USD: ${gold_price:.2f}
', unsafe_allow_html=True) # Function to display the user's financial overview def display_financial_overview(username, conn): st.subheader("Your Financial Overview") total_value, invested_value, initial_balance = calculate_total_value(username, conn) st.markdown(f'
Total Value (Including Investments): ${total_value:.2f}
', unsafe_allow_html=True) st.markdown(f'
Invested Value: ${invested_value:.2f}
', unsafe_allow_html=True) st.markdown(f'
Initial Investment: ${initial_balance:.2f}
', unsafe_allow_html=True) # Reintroducing get_user_data with initial_investment def get_user_data(username, conn): c = conn.cursor() c.execute("SELECT balance, currency, initial_balance FROM users WHERE username = ?", (username,)) balance, currency, initial_balance = c.fetchone() c.execute("SELECT ticker, shares, initial_investment FROM portfolios WHERE username = ?", (username,)) portfolio_data = c.fetchall() portfolio = {ticker: {"shares": shares, "initial_investment": initial_investment} for ticker, shares, initial_investment in portfolio_data} return balance, currency, initial_balance, portfolio # Function to calculate total value with comparison to initial investment def calculate_total_value(username, conn): balance, currency, initial_balance, portfolio = get_user_data(username, conn) total_value = balance invested_value = 0 for ticker, data in portfolio.items(): if data['shares'] > 0: stock_data = get_stock_data(ticker, '1d') if stock_data is not None: last_close = stock_data['Close'].iloc[-1] invested_value += last_close * data['shares'] total_value += invested_value total_value = convert_currency(total_value, 'USD', currency) invested_value = convert_currency(invested_value, 'USD', currency) initial_balance_converted = convert_currency(initial_balance, 'USD', currency) return total_value, invested_value, initial_balance_converted # Function to buy stock def buy_stock(username, ticker, amount, conn, currency): current_price = get_stock_data(ticker, period='1d') if current_price is None or current_price.empty: return "Invalid ticker symbol." total_cost = current_price['Close'].iloc[-1] * amount c = conn.cursor() c.execute("SELECT balance, currency FROM users WHERE username = ?", (username,)) user_balance, user_currency = c.fetchone() total_cost_in_user_currency = convert_currency(total_cost, 'USD', user_currency) if user_balance < total_cost_in_user_currency: return "Insufficient funds." new_balance = user_balance - total_cost_in_user_currency c.execute("UPDATE users SET balance = ? WHERE username = ?", (new_balance, username)) c.execute("SELECT shares FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker)) existing_shares = c.fetchone() if existing_shares: new_shares = existing_shares[0] + amount c.execute("UPDATE portfolios SET shares = ?, initial_investment = initial_investment + ? WHERE username = ? AND ticker = ?", (new_shares, total_cost, username, ticker)) else: c.execute("INSERT INTO portfolios (username, ticker, shares, initial_investment) VALUES (?, ?, ?, ?)", (username, ticker, amount, total_cost)) c.execute("INSERT INTO transactions (username, action, ticker, amount, price) VALUES (?, ?, ?, ?, ?)", (username, 'Buy', ticker, amount, current_price['Close'].iloc[-1])) conn.commit() return f"Successfully bought {amount} shares of {ticker} for ${total_cost_in_user_currency:.2f} {user_currency}" # Function to sell stock def sell_stock(username, ticker, amount, conn, currency): current_price = get_stock_data(ticker, period='1d') if current_price is None or current_price.empty: return "Invalid ticker symbol." total_value = current_price['Close'].iloc[-1] * amount c = conn.cursor() c.execute("SELECT shares FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker)) existing_shares = c.fetchone() if not existing_shares or existing_shares[0] < amount: return "Insufficient shares to sell." new_shares = existing_shares[0] - amount if new_shares == 0: c.execute("DELETE FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker)) else: c.execute("UPDATE portfolios SET shares = ? WHERE username = ? AND ticker = ?", (new_shares, username, ticker)) c.execute("SELECT balance, currency FROM users WHERE username = ?", (username,)) user_balance, user_currency = c.fetchone() total_value_in_user_currency = convert_currency(total_value, 'USD', user_currency) new_balance = user_balance + total_value_in_user_currency c.execute("UPDATE users SET balance = ? WHERE username = ?", (new_balance, username)) c.execute("INSERT INTO transactions (username, action, ticker, amount, price) VALUES (?, ?, ?, ?, ?)", (username, 'Sell', ticker, amount, current_price['Close'].iloc[-1])) conn.commit() return f"Successfully sold {amount} shares of {ticker} for ${total_value_in_user_currency:.2f} {user_currency}" # Main function with enhanced layout and features def main(): ensure_database_schema() initialize_music() if 'logged_in' not in st.session_state: st.session_state.logged_in = False if 'username' not in st.session_state: st.session_state.username = None st.markdown('
Charging Bull Trader
', unsafe_allow_html=True) if not st.session_state.logged_in: st.markdown('
Login
', unsafe_allow_html=True) username_input = st.text_input("Username") password_input = st.text_input("Password", type="password") if st.button("Login"): conn = setup_database() c = conn.cursor() c.execute("SELECT * FROM users WHERE username=? AND password=?", (username_input, hash_password(password_input))) if c.fetchone(): st.session_state.logged_in = True st.session_state.username = username_input else: st.error("Invalid username or password") # Here’s the message informing users to click twice if needed st.write("**Note:** If the login does not work, please click the button twice.") if st.button("Create Account"): conn = setup_database() c = conn.cursor() try: c.execute("INSERT INTO users (username, password, balance, initial_balance, currency) VALUES (?, ?, ?, ?, ?)", (username_input, hash_password(password_input), INITIAL_BALANCE, INITIAL_BALANCE, DEFAULT_CURRENCY)) for ticker in STOCK_LIST: c.execute("INSERT INTO portfolios (username, ticker, shares, initial_investment) VALUES (?, ?, 0, 0)", (username_input, ticker)) conn.commit() st.success("Account created successfully!") except sqlite3.IntegrityError: st.error("Username already exists.") else: username = st.session_state.username conn = setup_database() st.sidebar.write(f"Welcome, {username}!") if st.sidebar.button("Logout"): st.session_state.logged_in = False st.session_state.username = None # Add the small Charging Bull image under the Logout button st.sidebar.image("trade.png", width=150) display_financial_overview(username, conn) col1, col2 = st.columns(2) with col1: st.markdown('
Personal Holdings and Stocks
', unsafe_allow_html=True) time_range = st.selectbox("Select Time Range for Holdings", list(TIME_RANGES.keys())) _, _, _, portfolio = get_user_data(username, conn) if portfolio: plot_holdings(portfolio, time_range) else: st.write("No stocks in your portfolio.") st.markdown('
Buy/Sell Stocks
', unsafe_allow_html=True) action = st.selectbox("Choose Action", ["Buy", "Sell"]) ticker = st.text_input("Ticker") amount = st.number_input("Amount", min_value=1, step=1) selected_currency = st.selectbox("Currency", CURRENCIES) if st.button(f"{action} Stocks"): if action == "Buy": result = buy_stock(username, ticker, amount, conn, selected_currency) else: result = sell_stock(username, ticker, amount, conn, selected_currency) st.write(result) with col2: market_status = get_market_status() market_overview_ticker(market_status) display_currency_and_gold() st.markdown('
Stock Lookup
', unsafe_allow_html=True) stock_ticker = st.text_input("Enter Stock Ticker for Lookup") time_range_lookup = st.selectbox("Select Time Range", list(TIME_RANGES.keys()), key="lookup") if st.button("Lookup"): df = get_stock_data(stock_ticker, period=TIME_RANGES[time_range_lookup]) if df is not None and not df.empty: fig = px.line(df, x='Date', y='Close', title=f"{stock_ticker} - {time_range_lookup}") st.plotly_chart(fig, use_container_width=True) else: st.write("No data available for this ticker.") if __name__ == "__main__": main()