Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files- app.py +82 -0
- requirements.txt +67 -0
- stocks_qty.csv +91 -0
- valuation.py +87 -0
app.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import yfinance as yf
|
3 |
+
import csv
|
4 |
+
import pandas as pd
|
5 |
+
import matplotlib.pyplot as plt
|
6 |
+
from valuation import parallel_portfolio_valuation,serialCal
|
7 |
+
|
8 |
+
def get_stock_price(stock_symbol):
|
9 |
+
try:
|
10 |
+
stock = yf.Ticker(stock_symbol)
|
11 |
+
stock_data = stock.history(period="1d")
|
12 |
+
latest_close = stock_data['Close'].iloc[-1]
|
13 |
+
return float(latest_close)
|
14 |
+
except Exception as e:
|
15 |
+
return 0
|
16 |
+
|
17 |
+
def getCompoanyInfo(portfolio):
|
18 |
+
#yfinance
|
19 |
+
company_data = []
|
20 |
+
|
21 |
+
for stock in portfolio:
|
22 |
+
try:
|
23 |
+
stock_info = yf.Ticker(stock['symbol']).info
|
24 |
+
# Collect key datasets
|
25 |
+
company_details = {
|
26 |
+
'symbol': stock['symbol'],
|
27 |
+
'company_name': stock_info.get('longName', 'N/A'),
|
28 |
+
'sector': stock_info.get('sector', 'N/A'),
|
29 |
+
'industry': stock_info.get('industry', 'N/A'),
|
30 |
+
'market_cap': stock_info.get('marketCap', 'N/A'),
|
31 |
+
'dividend_yield': stock_info.get('dividendYield', 'N/A'),
|
32 |
+
'pe_ratio': stock_info.get('trailingPE', 'N/A'),
|
33 |
+
'52_week_high': stock_info.get('fiftyTwoWeekHigh', 'N/A'),
|
34 |
+
'52_week_low': stock_info.get('fiftyTwoWeekLow', 'N/A'),
|
35 |
+
'price': round(stock['price'],2),
|
36 |
+
'quantity': stock['quantity'],
|
37 |
+
}
|
38 |
+
|
39 |
+
company_data.append(company_details)
|
40 |
+
|
41 |
+
except Exception as e:
|
42 |
+
print(f"Error fetching data for {stock['symbol']}: {e}")
|
43 |
+
continue
|
44 |
+
|
45 |
+
return pd.DataFrame(company_data)
|
46 |
+
|
47 |
+
def calculatePortfolioValue(file:str):
|
48 |
+
portfolio = []
|
49 |
+
"""
|
50 |
+
portfolio = [
|
51 |
+
{'symbol': 'AAPL', 'price': 160, 'quantity': 100},
|
52 |
+
{'symbol': 'GOOG', 'price': 2000, 'quantity': 50},
|
53 |
+
{'symbol': 'MSFT', 'price': 300, 'quantity': 200},
|
54 |
+
|
55 |
+
]
|
56 |
+
"""
|
57 |
+
with open(file,"r") as fr:
|
58 |
+
reader = csv.reader(fr)
|
59 |
+
for i in list(reader)[1:]:
|
60 |
+
#print({'symbol':i[0],'price':get_stock_price(i[0]),'quantity':i[1]})
|
61 |
+
portfolio.append({'symbol':i[0],'price':get_stock_price(i[0]),'quantity':float(i[1])})
|
62 |
+
|
63 |
+
return str(round(parallel_portfolio_valuation(portfolio),2)) + " $", getCompoanyInfo(portfolio)
|
64 |
+
|
65 |
+
|
66 |
+
def startGradio():
|
67 |
+
with gr.Blocks() as demo:
|
68 |
+
text1 = gr.Text(value ="Stock Portfolio Valuation using multiprocessing",interactive=False,show_label=False)
|
69 |
+
with gr.Column():
|
70 |
+
file = gr.File()
|
71 |
+
df = gr.Dataframe()
|
72 |
+
parallelValue = gr.Textbox(label="Portfolio Valuation through the process of paralleization")
|
73 |
+
bt = gr.Button()
|
74 |
+
bt.click(fn=calculatePortfolioValue,inputs=[file],outputs=[parallelValue,df])
|
75 |
+
demo.load()
|
76 |
+
|
77 |
+
demo.launch()
|
78 |
+
|
79 |
+
|
80 |
+
if __name__ == "__main__":
|
81 |
+
startGradio()
|
82 |
+
#calculatePortfolioValue("/Users/sarathrajan/Desktop/HPCUI/stocks_qty.csv")
|
requirements.txt
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==23.2.1
|
2 |
+
annotated-types==0.7.0
|
3 |
+
anyio==4.6.0
|
4 |
+
beautifulsoup4==4.12.3
|
5 |
+
certifi==2024.8.30
|
6 |
+
charset-normalizer==3.3.2
|
7 |
+
click==8.1.7
|
8 |
+
contourpy==1.3.0
|
9 |
+
cycler==0.12.1
|
10 |
+
fastapi==0.115.0
|
11 |
+
ffmpy==0.4.0
|
12 |
+
filelock==3.16.1
|
13 |
+
fonttools==4.54.1
|
14 |
+
frozendict==2.4.4
|
15 |
+
fsspec==2024.9.0
|
16 |
+
gradio==4.44.0
|
17 |
+
gradio_client==1.3.0
|
18 |
+
h11==0.14.0
|
19 |
+
html5lib==1.1
|
20 |
+
httpcore==1.0.5
|
21 |
+
httpx==0.27.2
|
22 |
+
huggingface-hub==0.25.1
|
23 |
+
idna==3.10
|
24 |
+
importlib_resources==6.4.5
|
25 |
+
Jinja2==3.1.4
|
26 |
+
kiwisolver==1.4.7
|
27 |
+
lxml==5.3.0
|
28 |
+
markdown-it-py==3.0.0
|
29 |
+
MarkupSafe==2.1.5
|
30 |
+
matplotlib==3.9.2
|
31 |
+
mdurl==0.1.2
|
32 |
+
multitasking==0.0.11
|
33 |
+
numpy==2.1.1
|
34 |
+
orjson==3.10.7
|
35 |
+
packaging==24.1
|
36 |
+
pandas==2.2.3
|
37 |
+
peewee==3.17.6
|
38 |
+
pillow==10.4.0
|
39 |
+
platformdirs==4.3.6
|
40 |
+
pydantic==2.9.2
|
41 |
+
pydantic_core==2.23.4
|
42 |
+
pydub==0.25.1
|
43 |
+
Pygments==2.18.0
|
44 |
+
pyparsing==3.1.4
|
45 |
+
python-dateutil==2.9.0.post0
|
46 |
+
python-multipart==0.0.12
|
47 |
+
pytz==2024.2
|
48 |
+
PyYAML==6.0.2
|
49 |
+
requests==2.32.3
|
50 |
+
rich==13.8.1
|
51 |
+
ruff==0.6.8
|
52 |
+
semantic-version==2.10.0
|
53 |
+
shellingham==1.5.4
|
54 |
+
six==1.16.0
|
55 |
+
sniffio==1.3.1
|
56 |
+
soupsieve==2.6
|
57 |
+
starlette==0.38.6
|
58 |
+
tomlkit==0.12.0
|
59 |
+
tqdm==4.66.5
|
60 |
+
typer==0.12.5
|
61 |
+
typing_extensions==4.12.2
|
62 |
+
tzdata==2024.2
|
63 |
+
urllib3==2.2.3
|
64 |
+
uvicorn==0.31.0
|
65 |
+
webencodings==0.5.1
|
66 |
+
websockets==12.0
|
67 |
+
yfinance==0.2.43
|
stocks_qty.csv
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Stock Symbol,Quantity
|
2 |
+
NVDA,76
|
3 |
+
V,16
|
4 |
+
NVDA,77
|
5 |
+
DIS,10
|
6 |
+
TSLA,83
|
7 |
+
AAPL,76
|
8 |
+
V,63
|
9 |
+
UNH,11
|
10 |
+
GOOGL,98
|
11 |
+
TSLA,88
|
12 |
+
NFLX,77
|
13 |
+
AAPL,22
|
14 |
+
WMT,59
|
15 |
+
PG,11
|
16 |
+
NVDA,83
|
17 |
+
PG,77
|
18 |
+
GOOGL,41
|
19 |
+
UNH,49
|
20 |
+
VZ,68
|
21 |
+
WMT,74
|
22 |
+
HD,82
|
23 |
+
VZ,11
|
24 |
+
NFLX,96
|
25 |
+
V,50
|
26 |
+
PG,68
|
27 |
+
PG,99
|
28 |
+
AMZN,51
|
29 |
+
V,14
|
30 |
+
HD,40
|
31 |
+
AAPL,98
|
32 |
+
GOOGL,63
|
33 |
+
HD,30
|
34 |
+
NVDA,57
|
35 |
+
BABA,38
|
36 |
+
WMT,47
|
37 |
+
NVDA,18
|
38 |
+
AMZN,22
|
39 |
+
BABA,52
|
40 |
+
PG,1
|
41 |
+
AMZN,5
|
42 |
+
NVDA,21
|
43 |
+
JNJ,84
|
44 |
+
DIS,9
|
45 |
+
WMT,55
|
46 |
+
GOOGL,93
|
47 |
+
NFLX,15
|
48 |
+
BABA,9
|
49 |
+
UNH,60
|
50 |
+
WMT,22
|
51 |
+
MSFT,14
|
52 |
+
PYPL,57
|
53 |
+
NVDA,28
|
54 |
+
MA,13
|
55 |
+
BABA,71
|
56 |
+
GOOGL,70
|
57 |
+
JNJ,43
|
58 |
+
WMT,97
|
59 |
+
GOOGL,81
|
60 |
+
AAPL,65
|
61 |
+
WMT,61
|
62 |
+
UNH,81
|
63 |
+
WMT,17
|
64 |
+
PG,99
|
65 |
+
GOOGL,98
|
66 |
+
DIS,92
|
67 |
+
NFLX,3
|
68 |
+
VZ,57
|
69 |
+
BABA,65
|
70 |
+
JNJ,1
|
71 |
+
NVDA,3
|
72 |
+
GOOGL,32
|
73 |
+
MSFT,92
|
74 |
+
PG,55
|
75 |
+
HD,21
|
76 |
+
UNH,57
|
77 |
+
NVDA,83
|
78 |
+
GOOGL,28
|
79 |
+
JNJ,30
|
80 |
+
TSLA,45
|
81 |
+
NVDA,65
|
82 |
+
DIS,44
|
83 |
+
MSFT,85
|
84 |
+
WMT,2
|
85 |
+
MSFT,9
|
86 |
+
NFLX,40
|
87 |
+
VZ,88
|
88 |
+
V,90
|
89 |
+
VZ,59
|
90 |
+
HD,50
|
91 |
+
PYPL,34
|
valuation.py
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import multiprocessing
|
2 |
+
|
3 |
+
import random
|
4 |
+
import math
|
5 |
+
|
6 |
+
|
7 |
+
def complex_stock_valuation(stock):
|
8 |
+
# Basic value
|
9 |
+
base_value = stock['price'] * stock['quantity']
|
10 |
+
|
11 |
+
# Simulate a more complex valuation model
|
12 |
+
pe_ratio = random.uniform(10, 30)
|
13 |
+
beta = random.uniform(0.5, 2.0)
|
14 |
+
risk_free_rate = 0.02 # 2% risk-free rate
|
15 |
+
market_return = 0.08 # 8% market return
|
16 |
+
|
17 |
+
# Calculate expected return using CAPM (Capital Asset Pricing Model)
|
18 |
+
expected_return = risk_free_rate + beta * (market_return - risk_free_rate)
|
19 |
+
|
20 |
+
# Calculate dividend yield
|
21 |
+
dividend_yield = random.uniform(0.01, 0.05)
|
22 |
+
|
23 |
+
# Dividend Discount Model
|
24 |
+
growth_rate = random.uniform(0.02, 0.10)
|
25 |
+
intrinsic_value = (stock['price'] * dividend_yield * (1 + growth_rate)) / (expected_return - growth_rate)
|
26 |
+
|
27 |
+
# Adjust value based on a weighted average of different factors
|
28 |
+
adjusted_value = (
|
29 |
+
base_value * 0.4 + # 40% weight to market value
|
30 |
+
intrinsic_value * 0.3 + # 30% weight to intrinsic value
|
31 |
+
(base_value / pe_ratio) * 0.2 + # 20% weight to earnings-based value
|
32 |
+
(base_value * (1 + expected_return)) * 0.1 # 10% weight to expected future value
|
33 |
+
)
|
34 |
+
|
35 |
+
# Add some non-linear complexity
|
36 |
+
volatility = random.uniform(0.1, 0.5)
|
37 |
+
adjusted_value *= math.exp(-volatility * beta)
|
38 |
+
|
39 |
+
# Simulate some intensive calculation
|
40 |
+
for _ in range(1000):
|
41 |
+
adjusted_value = math.sqrt((adjusted_value ** 2 + base_value ** 2) / 2)
|
42 |
+
|
43 |
+
return adjusted_value
|
44 |
+
|
45 |
+
def calculate_value(portfolio_slice, queue):
|
46 |
+
total_value = 0
|
47 |
+
for stock in portfolio_slice:
|
48 |
+
total_value += complex_stock_valuation(stock)
|
49 |
+
queue.put(total_value) # Send the result back to the main process
|
50 |
+
|
51 |
+
def parallel_portfolio_valuation(portfolio, num_processes=2):
|
52 |
+
portfolio_size = len(portfolio)
|
53 |
+
num_processes = min(num_processes, portfolio_size) # Ensure num_processes doesn't exceed portfolio_size
|
54 |
+
chunk_size = max(1, portfolio_size // num_processes)
|
55 |
+
processes = []
|
56 |
+
queue = multiprocessing.Queue() # Queue for collecting results
|
57 |
+
chunks_processed = 0 # Keep track of how many chunks we've processed
|
58 |
+
|
59 |
+
for i in range(num_processes):
|
60 |
+
start_index = i * chunk_size
|
61 |
+
end_index = min((i + 1) * chunk_size, portfolio_size)
|
62 |
+
portfolio_slice = portfolio[start_index:end_index]
|
63 |
+
if portfolio_slice: # Only start a process if there are items to process
|
64 |
+
process = multiprocessing.Process(target=calculate_value, args=(portfolio_slice, queue))
|
65 |
+
processes.append(process)
|
66 |
+
process.start()
|
67 |
+
chunks_processed += 1 # Increment for each chunk we process
|
68 |
+
|
69 |
+
total_value = 0
|
70 |
+
# Collect results for each chunk we processed
|
71 |
+
for _ in range(chunks_processed):
|
72 |
+
total_value += queue.get()
|
73 |
+
|
74 |
+
# Wait for all processes to finish
|
75 |
+
for process in processes:
|
76 |
+
process.join()
|
77 |
+
|
78 |
+
return total_value
|
79 |
+
|
80 |
+
|
81 |
+
|
82 |
+
def serialCal(portfolio):
|
83 |
+
total = 0
|
84 |
+
for stock in portfolio:
|
85 |
+
total += complex_stock_valuation(stock)
|
86 |
+
return total
|
87 |
+
|