Aluode commited on
Commit
9c9af70
·
verified ·
1 Parent(s): e94ca69

Upload 6 files

Browse files
Files changed (7) hide show
  1. .gitattributes +1 -0
  2. LICENSE +21 -0
  3. README.md +70 -11
  4. app.py +512 -0
  5. requirements.txt +9 -0
  6. trade.mp3 +3 -0
  7. trade.png +0 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ trade.mp3 filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 anttiluode
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,13 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
- title: ChargingBullTrader
3
- emoji: 🚀
4
- colorFrom: yellow
5
- colorTo: indigo
6
- sdk: streamlit
7
- sdk_version: 1.37.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
+
2
+ # Charging Bull Trader
3
+
4
+ ## Overview
5
+
6
+ Welcome to **Charging Bull Trader**, a trading simulation game where you start with a balance of $10,000 and engage in buying and selling stocks. The game also allows you to seek news about stocks and tracks your performance over time.
7
+
8
+ ## Features
9
+
10
+ - **Trade Stocks:** Buy and sell stocks from a predefined list.
11
+ - **Track Performance:** View your current cash balance, portfolio, and overall financial status.
12
+ - **Stock News:** Get the latest news about specific stocks.
13
+ - **Portfolio Performance:** Analyze the historical performance of stocks in your portfolio.
14
+ - **Database Tracking:** Each player's activity is recorded in a SQLite database, tracking transactions and performance.
15
+
16
+ ## Getting Started
17
+
18
+ ### Prerequisites
19
+
20
+ Ensure you have Python 3.8 or higher installed on your system. You'll also need to install the required Python packages, which can be done using the `requirements.txt` file.
21
+
22
+ ### Installation
23
+
24
+ 1. Clone this repository to your local machine:
25
+ ```sh
26
+ git clone https://github.com/anttiluode/chargingbulltrader.git
27
+ cd chargingbulltrader
28
+ ```
29
+
30
+ 2. Create a virtual environment (optional but recommended):
31
+
32
+ Use whatever software like Anaconda, or do not use a virtual environment.
33
+
34
+ 3. Install the required packages:
35
+ ```sh
36
+ pip install -r requirements.txt
37
+ ```
38
+
39
+ 4. Run the Streamlit app:
40
+ ```sh
41
+ python -m streamlit run app.py
42
+ ```
43
+ or
44
+ ```sh
45
+ streamlit run app.py
46
+ ```
47
+
48
+ 6. Create account and log in. You may have to press the log in button twice for some reason.
49
+
50
+ ## Usage
51
+
52
+ 1. **Create an Account:** Use the sidebar to create an account with a username and password.
53
+ 2. **Login:** Once registered, log in using your credentials.
54
+ 3. **Trade Stocks:** Buy and sell stocks through the trading interface.
55
+ 4. **Check Performance:** View your current balance, portfolio, and transaction history.
56
+ 5. **Explore News:** Get the latest news for stocks of interest.
57
+ 6. **Analyze Portfolio:** View and analyze the historical performance of stocks in your portfolio.
58
+
59
+ ## Credits
60
+
61
+ - **Author:** Created by Antti Luode with assistance from ChatGPT.
62
+ - **Image:** Generated by Flux.1 AI.
63
+ - **Music:** Composed by Udio.
64
+
65
+ ## License
66
+
67
+ This project is licensed under the MIT License - see the LICENSE file for details.
68
+
69
  ---
 
 
 
 
 
 
 
 
 
 
70
 
71
+ Enjoy trading and tracking your stock performance with **Charging Bull Trader**!
72
+
app.py ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import sqlite3
3
+ import hashlib
4
+ import threading
5
+ import pygame
6
+ from yfinance import Ticker
7
+ import pandas as pd
8
+ import plotly.express as px
9
+ import requests
10
+ from newsapi import NewsApiClient
11
+ import os
12
+ from pathlib import Path
13
+ import base64
14
+ from datetime import datetime
15
+ import time
16
+
17
+ # Constants
18
+ INITIAL_BALANCE = 10000
19
+ UPDATE_INTERVAL = 60
20
+ DEFAULT_CURRENCY = 'USD'
21
+ STOCK_LIST = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "NVDA", "BRK-B", "V", "JPM"]
22
+ TIME_RANGES = {
23
+ '1 Day': '1d',
24
+ '5 Days': '5d',
25
+ '1 Month': '1mo',
26
+ '3 Months': '3mo',
27
+ '6 Months': '6mo',
28
+ '1 Year': '1y',
29
+ '5 Years': '5y',
30
+ '10 Years': '10y',
31
+ 'Max': 'max'
32
+ }
33
+ CURRENCIES = ["USD", "EUR", "GBP", "JPY", "AUD"]
34
+
35
+ # Initialize NewsAPI Client
36
+ NEWS_API_KEY = 'your_newsapi_key_here'
37
+ newsapi = NewsApiClient(api_key=NEWS_API_KEY)
38
+
39
+ # Initialize pygame for music
40
+ def initialize_music():
41
+ if 'music_initialized' not in st.session_state:
42
+ try:
43
+ pygame.mixer.init()
44
+ pygame.mixer.music.load('trade.mp3')
45
+ pygame.mixer.music.play(-1)
46
+ st.session_state['music_initialized'] = True
47
+ except pygame.error as e:
48
+ st.error(f"Failed to load or play the music: {str(e)}. Please check that 'trade.mp3' exists in the correct directory.")
49
+
50
+ # Database setup
51
+ def setup_database():
52
+ conn = sqlite3.connect('trading_game.db', check_same_thread=False)
53
+ cursor = conn.cursor()
54
+
55
+ # Create tables if they don't exist
56
+ cursor.execute('''
57
+ CREATE TABLE IF NOT EXISTS users (
58
+ username TEXT PRIMARY KEY,
59
+ password TEXT,
60
+ balance REAL,
61
+ initial_balance REAL,
62
+ currency TEXT DEFAULT 'USD'
63
+ )
64
+ ''')
65
+
66
+ cursor.execute('''
67
+ CREATE TABLE IF NOT EXISTS portfolios (
68
+ username TEXT,
69
+ ticker TEXT,
70
+ shares REAL,
71
+ initial_investment REAL,
72
+ PRIMARY KEY (username, ticker),
73
+ FOREIGN KEY (username) REFERENCES users (username)
74
+ )
75
+ ''')
76
+
77
+ cursor.execute('''
78
+ CREATE TABLE IF NOT EXISTS financial_logs (
79
+ username TEXT,
80
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
81
+ total_value REAL,
82
+ FOREIGN KEY (username) REFERENCES users (username)
83
+ )
84
+ ''')
85
+
86
+ cursor.execute('''
87
+ CREATE TABLE IF NOT EXISTS transactions (
88
+ username TEXT,
89
+ action TEXT,
90
+ ticker TEXT,
91
+ amount REAL,
92
+ price REAL,
93
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
94
+ )
95
+ ''')
96
+
97
+ conn.commit()
98
+ return conn
99
+
100
+ # Ensure Database Schema
101
+ def ensure_database_schema():
102
+ conn = sqlite3.connect('trading_game.db')
103
+ c = conn.cursor()
104
+
105
+ # Ensure the users table exists and has the required columns
106
+ c.execute('''
107
+ CREATE TABLE IF NOT EXISTS users (
108
+ username TEXT PRIMARY KEY,
109
+ password TEXT,
110
+ balance REAL,
111
+ initial_balance REAL,
112
+ currency TEXT DEFAULT 'USD'
113
+ )
114
+ ''')
115
+
116
+ # Check if the initial_balance column exists, if not, add it
117
+ c.execute("PRAGMA table_info(users)")
118
+ columns = [column[1] for column in c.fetchall()]
119
+ if 'initial_balance' not in columns:
120
+ c.execute(f"ALTER TABLE users ADD COLUMN initial_balance REAL DEFAULT {INITIAL_BALANCE}")
121
+ conn.commit()
122
+
123
+ # Ensure the portfolios table exists and has the required columns
124
+ c.execute('''
125
+ CREATE TABLE IF NOT EXISTS portfolios (
126
+ username TEXT,
127
+ ticker TEXT,
128
+ shares REAL,
129
+ initial_investment REAL,
130
+ PRIMARY KEY (username, ticker),
131
+ FOREIGN KEY (username) REFERENCES users (username)
132
+ )
133
+ ''')
134
+
135
+ # Check if the initial_investment column exists, if not, add it
136
+ c.execute("PRAGMA table_info(portfolios)")
137
+ columns = [column[1] for column in c.fetchall()]
138
+ if 'initial_investment' not in columns:
139
+ c.execute(f"ALTER TABLE portfolios ADD COLUMN initial_investment REAL")
140
+ conn.commit()
141
+
142
+ conn.close()
143
+
144
+ # Function to hash passwords
145
+ def hash_password(password):
146
+ return hashlib.sha256(password.encode()).hexdigest()
147
+
148
+ # Function to set the background image
149
+ def set_background_image(image_path):
150
+ if Path(image_path).exists():
151
+ try:
152
+ with open(image_path, "rb") as img_file:
153
+ encoded_img = base64.b64encode(img_file.read()).decode('utf-8')
154
+ st.markdown(
155
+ f"""
156
+ <style>
157
+ .stApp {{
158
+ position: relative;
159
+ background-image: url(data:image/png;base64,{encoded_img});
160
+ background-size: cover;
161
+ background-position: center;
162
+ background-repeat: no-repeat;
163
+ }}
164
+ .stApp::before {{
165
+ content: '';
166
+ position: absolute;
167
+ top: 0;
168
+ left: 0;
169
+ right: 0;
170
+ bottom: 0;
171
+ background-color: rgba(255, 255, 255, 0.8); /* White overlay with 0.8 opacity */
172
+ z-index: 0;
173
+ }}
174
+ .main-title, .sub-title, .metric-box, .blue-background {{
175
+ position: relative;
176
+ z-index: 1;
177
+ }}
178
+ </style>
179
+ """,
180
+ unsafe_allow_html=True
181
+ )
182
+ except Exception as e:
183
+ st.error(f"Error loading image: {str(e)}")
184
+ else:
185
+ st.error('Image not found. Please check the file name and path.')
186
+
187
+ # Function to fetch the current price of stocks or currencies
188
+ def get_stock_data(ticker, period='1d'):
189
+ try:
190
+ stock = Ticker(ticker)
191
+ df = stock.history(period=period)
192
+ if not df.empty:
193
+ df.reset_index(inplace=True)
194
+ return df
195
+ else:
196
+ return None
197
+ except Exception as e:
198
+ print(f"Error fetching data: {str(e)}")
199
+ return None
200
+
201
+ # Function to convert currency
202
+ def convert_currency(amount, from_currency, to_currency):
203
+ if from_currency == to_currency:
204
+ return amount
205
+ try:
206
+ response = requests.get(f'https://api.exchangerate-api.com/v4/latest/{from_currency}')
207
+ rates = response.json()['rates']
208
+ conversion_rate = rates.get(to_currency, 1)
209
+ return amount * conversion_rate
210
+ except Exception as e:
211
+ print(f"Error converting currency: {str(e)}")
212
+ return amount
213
+
214
+ # Function to get market status
215
+ def get_market_status():
216
+ market_status = {}
217
+ for ticker in STOCK_LIST:
218
+ df = get_stock_data(ticker, period='1d')
219
+ if df is not None:
220
+ open_price = df['Open'].iloc[0]
221
+ current_price = df['Close'].iloc[-1]
222
+ change = (current_price - open_price) / open_price * 100
223
+ market_status[ticker] = change
224
+ return market_status
225
+
226
+ # Function to fetch and plot historical data for holdings
227
+ def plot_holdings(holdings, time_range):
228
+ fig = px.line(title=f"Holdings - {time_range}")
229
+ for ticker in holdings:
230
+ df = get_stock_data(ticker, period=TIME_RANGES[time_range])
231
+ if df is not None and not df.empty:
232
+ initial_investment = holdings[ticker]['initial_investment']
233
+ fig.add_scatter(x=df['Date'], y=df['Close'], mode='lines', name=f"{ticker} (Invested: ${initial_investment:.2f})")
234
+ st.plotly_chart(fig, use_container_width=True)
235
+
236
+ # Function to display the market overview ticker
237
+ def market_overview_ticker(market_status):
238
+ ticker_text = ''.join([
239
+ f"<span style='color: {'green' if change > 0 else 'red'};'>{ticker}: {change:.2f}%</span> | "
240
+ for ticker, change in market_status.items()
241
+ ])
242
+ st.markdown(f"""
243
+ <style>
244
+ .ticker {{
245
+ overflow: hidden;
246
+ white-space: nowrap;
247
+ width: 100%;
248
+ background-color: black;
249
+ color: white;
250
+ padding: 5px;
251
+ font-size: 14px;
252
+ }}
253
+ .ticker span {{
254
+ display: inline-block;
255
+ animation: ticker 20s linear infinite;
256
+ }}
257
+ @keyframes ticker {{
258
+ 0% {{ transform: translateX(100%); }}
259
+ 100% {{ transform: translateX(-100%); }}
260
+ }}
261
+ </style>
262
+ <div class="ticker">
263
+ <span>{ticker_text}</span>
264
+ </div>
265
+ """, unsafe_allow_html=True)
266
+
267
+ # Function to fetch currency values
268
+ def get_currency_values():
269
+ currencies = ['EUR', 'GBP', 'JPY', 'AUD']
270
+ currency_data = {}
271
+ for currency in currencies:
272
+ rate = convert_currency(1, 'USD', currency)
273
+ currency_data[currency] = rate
274
+ return currency_data
275
+
276
+ # Function to fetch gold price from yfinance
277
+ def get_gold_price():
278
+ gold_ticker = Ticker("GC=F")
279
+ df = gold_ticker.history(period="1d")
280
+ if not df.empty:
281
+ return df['Close'].iloc[-1]
282
+ else:
283
+ st.error("Failed to fetch the gold price.")
284
+ return None
285
+
286
+ # Function to display currency and gold values
287
+ def display_currency_and_gold():
288
+ st.markdown('<div class="sub-title">Currency and Gold Values</div>', unsafe_allow_html=True)
289
+ currency_data = get_currency_values()
290
+ gold_price = get_gold_price()
291
+ for currency, value in currency_data.items():
292
+ st.markdown(f'<div class="metric-box">{currency}/USD: {value:.2f}</div>', unsafe_allow_html=True)
293
+ if gold_price:
294
+ st.markdown(f'<div class="metric-box">Gold/USD: ${gold_price:.2f}</div>', unsafe_allow_html=True)
295
+
296
+ # Function to display the user's financial overview
297
+ def display_financial_overview(username, conn):
298
+ st.subheader("Your Financial Overview")
299
+ total_value, invested_value, initial_balance = calculate_total_value(username, conn)
300
+ st.markdown(f'<div class="metric-box">Total Value (Including Investments): ${total_value:.2f}</div>', unsafe_allow_html=True)
301
+ st.markdown(f'<div class="metric-box">Invested Value: ${invested_value:.2f}</div>', unsafe_allow_html=True)
302
+ st.markdown(f'<div class="metric-box">Initial Investment: ${initial_balance:.2f}</div>', unsafe_allow_html=True)
303
+
304
+ # Reintroducing get_user_data with initial_investment
305
+ def get_user_data(username, conn):
306
+ c = conn.cursor()
307
+ c.execute("SELECT balance, currency, initial_balance FROM users WHERE username = ?", (username,))
308
+ balance, currency, initial_balance = c.fetchone()
309
+
310
+ c.execute("SELECT ticker, shares, initial_investment FROM portfolios WHERE username = ?", (username,))
311
+ portfolio_data = c.fetchall()
312
+ portfolio = {ticker: {"shares": shares, "initial_investment": initial_investment} for ticker, shares, initial_investment in portfolio_data}
313
+
314
+ return balance, currency, initial_balance, portfolio
315
+
316
+ # Function to calculate total value with comparison to initial investment
317
+ def calculate_total_value(username, conn):
318
+ balance, currency, initial_balance, portfolio = get_user_data(username, conn)
319
+ total_value = balance
320
+ invested_value = 0
321
+ for ticker, data in portfolio.items():
322
+ if data['shares'] > 0:
323
+ stock_data = get_stock_data(ticker, '1d')
324
+ if stock_data is not None:
325
+ last_close = stock_data['Close'].iloc[-1]
326
+ invested_value += last_close * data['shares']
327
+ total_value += invested_value
328
+ total_value = convert_currency(total_value, 'USD', currency)
329
+ invested_value = convert_currency(invested_value, 'USD', currency)
330
+ initial_balance_converted = convert_currency(initial_balance, 'USD', currency)
331
+ return total_value, invested_value, initial_balance_converted
332
+
333
+ # Function to buy stock
334
+ def buy_stock(username, ticker, amount, conn, currency):
335
+ current_price = get_stock_data(ticker, period='1d')
336
+
337
+ if current_price is None or current_price.empty:
338
+ return "Invalid ticker symbol."
339
+
340
+ total_cost = current_price['Close'].iloc[-1] * amount
341
+
342
+ c = conn.cursor()
343
+ c.execute("SELECT balance, currency FROM users WHERE username = ?", (username,))
344
+ user_balance, user_currency = c.fetchone()
345
+
346
+ total_cost_in_user_currency = convert_currency(total_cost, 'USD', user_currency)
347
+
348
+ if user_balance < total_cost_in_user_currency:
349
+ return "Insufficient funds."
350
+
351
+ new_balance = user_balance - total_cost_in_user_currency
352
+ c.execute("UPDATE users SET balance = ? WHERE username = ?", (new_balance, username))
353
+
354
+ c.execute("SELECT shares FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker))
355
+ existing_shares = c.fetchone()
356
+
357
+ if existing_shares:
358
+ new_shares = existing_shares[0] + amount
359
+ c.execute("UPDATE portfolios SET shares = ?, initial_investment = initial_investment + ? WHERE username = ? AND ticker = ?", (new_shares, total_cost, username, ticker))
360
+ else:
361
+ c.execute("INSERT INTO portfolios (username, ticker, shares, initial_investment) VALUES (?, ?, ?, ?)", (username, ticker, amount, total_cost))
362
+
363
+ c.execute("INSERT INTO transactions (username, action, ticker, amount, price) VALUES (?, ?, ?, ?, ?)",
364
+ (username, 'Buy', ticker, amount, current_price['Close'].iloc[-1]))
365
+
366
+ conn.commit()
367
+ return f"Successfully bought {amount} shares of {ticker} for ${total_cost_in_user_currency:.2f} {user_currency}"
368
+
369
+ # Function to sell stock
370
+ def sell_stock(username, ticker, amount, conn, currency):
371
+ current_price = get_stock_data(ticker, period='1d')
372
+
373
+ if current_price is None or current_price.empty:
374
+ return "Invalid ticker symbol."
375
+
376
+ total_value = current_price['Close'].iloc[-1] * amount
377
+
378
+ c = conn.cursor()
379
+ c.execute("SELECT shares FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker))
380
+ existing_shares = c.fetchone()
381
+
382
+ if not existing_shares or existing_shares[0] < amount:
383
+ return "Insufficient shares to sell."
384
+
385
+ new_shares = existing_shares[0] - amount
386
+ if new_shares == 0:
387
+ c.execute("DELETE FROM portfolios WHERE username = ? AND ticker = ?", (username, ticker))
388
+ else:
389
+ c.execute("UPDATE portfolios SET shares = ? WHERE username = ? AND ticker = ?", (new_shares, username, ticker))
390
+
391
+ c.execute("SELECT balance, currency FROM users WHERE username = ?", (username,))
392
+ user_balance, user_currency = c.fetchone()
393
+ total_value_in_user_currency = convert_currency(total_value, 'USD', user_currency)
394
+ new_balance = user_balance + total_value_in_user_currency
395
+ c.execute("UPDATE users SET balance = ? WHERE username = ?", (new_balance, username))
396
+
397
+ c.execute("INSERT INTO transactions (username, action, ticker, amount, price) VALUES (?, ?, ?, ?, ?)",
398
+ (username, 'Sell', ticker, amount, current_price['Close'].iloc[-1]))
399
+
400
+ conn.commit()
401
+ return f"Successfully sold {amount} shares of {ticker} for ${total_value_in_user_currency:.2f} {user_currency}"
402
+
403
+ # Main function with enhanced layout and features
404
+ def main():
405
+ ensure_database_schema()
406
+ initialize_music()
407
+
408
+ if 'logged_in' not in st.session_state:
409
+ st.session_state.logged_in = False
410
+ if 'username' not in st.session_state:
411
+ st.session_state.username = None
412
+
413
+ st.markdown('<div class="main-title" style="background-color: blue; color: white; padding: 10px;">Charging Bull Trader</div>', unsafe_allow_html=True)
414
+
415
+ if not st.session_state.logged_in:
416
+ st.markdown('<div class="sub-title">Login</div>', unsafe_allow_html=True)
417
+ username_input = st.text_input("Username")
418
+ password_input = st.text_input("Password", type="password")
419
+ if st.button("Login"):
420
+ conn = setup_database()
421
+ c = conn.cursor()
422
+ c.execute("SELECT * FROM users WHERE username=? AND password=?", (username_input, hash_password(password_input)))
423
+ if c.fetchone():
424
+ st.session_state.logged_in = True
425
+ st.session_state.username = username_input
426
+ else:
427
+ st.error("Invalid username or password")
428
+
429
+ if st.button("Create Account"):
430
+ conn = setup_database()
431
+ c = conn.cursor()
432
+ try:
433
+ c.execute("INSERT INTO users (username, password, balance, initial_balance, currency) VALUES (?, ?, ?, ?, ?)",
434
+ (username_input, hash_password(password_input), INITIAL_BALANCE, INITIAL_BALANCE, DEFAULT_CURRENCY))
435
+ for ticker in STOCK_LIST:
436
+ c.execute("INSERT INTO portfolios (username, ticker, shares, initial_investment) VALUES (?, ?, 0, 0)", (username_input, ticker))
437
+ conn.commit()
438
+ st.success("Account created successfully!")
439
+ except sqlite3.IntegrityError:
440
+ st.error("Username already exists.")
441
+ else:
442
+ username = st.session_state.username
443
+ conn = setup_database()
444
+ st.sidebar.write(f"Welcome, {username}!")
445
+ if st.sidebar.button("Logout"):
446
+ st.session_state.logged_in = False
447
+ st.session_state.username = None
448
+
449
+ # Add the small Charging Bull image under the Logout button
450
+ st.sidebar.image("trade.png", width=150)
451
+
452
+ display_financial_overview(username, conn)
453
+
454
+ col1, col2 = st.columns(2)
455
+
456
+ with col1:
457
+ st.markdown('<div class="sub-title">Personal Holdings and Stocks</div>', unsafe_allow_html=True)
458
+ time_range = st.selectbox("Select Time Range for Holdings", list(TIME_RANGES.keys()))
459
+ _, _, _, portfolio = get_user_data(username, conn)
460
+ if portfolio:
461
+ plot_holdings(portfolio, time_range)
462
+ else:
463
+ st.write("No stocks in your portfolio.")
464
+
465
+ st.markdown('<div class="sub-title">Buy/Sell Stocks</div>', unsafe_allow_html=True)
466
+ action = st.selectbox("Choose Action", ["Buy", "Sell"])
467
+ ticker = st.text_input("Ticker")
468
+ amount = st.number_input("Amount", min_value=1, step=1)
469
+ selected_currency = st.selectbox("Currency", CURRENCIES)
470
+
471
+ if st.button(f"{action} Stocks"):
472
+ if action == "Buy":
473
+ result = buy_stock(username, ticker, amount, conn, selected_currency)
474
+ else:
475
+ result = sell_stock(username, ticker, amount, conn, selected_currency)
476
+ st.write(result)
477
+
478
+ with col2:
479
+ market_status = get_market_status()
480
+ market_overview_ticker(market_status)
481
+ display_currency_and_gold()
482
+
483
+ st.markdown('<div class="sub-title">Stock Lookup</div>', unsafe_allow_html=True)
484
+ stock_ticker = st.text_input("Enter Stock Ticker for Lookup")
485
+ time_range_lookup = st.selectbox("Select Time Range", list(TIME_RANGES.keys()), key="lookup")
486
+ if st.button("Lookup"):
487
+ df = get_stock_data(stock_ticker, period=TIME_RANGES[time_range_lookup])
488
+ if df is not None and not df.empty:
489
+ fig = px.line(df, x='Date', y='Close', title=f"{stock_ticker} - {time_range_lookup}")
490
+ st.plotly_chart(fig, use_container_width=True)
491
+ else:
492
+ st.write("No data available for this ticker.")
493
+
494
+ st.markdown('<div class="sub-title">Stock Market News</div>', unsafe_allow_html=True)
495
+ news_ticker = st.text_input("Enter Stock Ticker for News")
496
+ news_api_key = st.text_input("Enter NewsAPI Key")
497
+ if st.button("Get News"):
498
+ if news_api_key:
499
+ newsapi = NewsApiClient(api_key=news_api_key)
500
+ articles = newsapi.get_everything(q=news_ticker, language='en', sort_by='publishedAt', page_size=5)
501
+ if articles:
502
+ for article in articles['articles']:
503
+ st.subheader(article['title'])
504
+ st.write(article['description'])
505
+ st.write(f"[Read more]({article['url']})")
506
+ else:
507
+ st.write("No news available for this ticker.")
508
+ else:
509
+ st.write("Please enter a valid NewsAPI key.")
510
+
511
+ if __name__ == "__main__":
512
+ main()
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ streamlit==1.37.1
2
+ pygame
3
+ yfinance
4
+ pandas
5
+ plotly
6
+ requests
7
+ newsapi-python
8
+ opencv-python-headless
9
+ numpy
trade.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d1d58b74b2932055654e2b516bf2dbbedc277309957d4fbefaa4d1a4c75c34a
3
+ size 5247493
trade.png ADDED