Skip to content

Commit 6c73862

Browse files
authored
Create Excel Update Sync.py
1 parent 6d5cb5a commit 6c73862

1 file changed

Lines changed: 353 additions & 0 deletions

File tree

Excel Update Sync.py

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
import requests
2+
from bs4 import BeautifulSoup
3+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
4+
import torch
5+
import numpy as np
6+
from datetime import datetime, timedelta
7+
import pandas as pd
8+
import yfinance as yf
9+
from langdetect import detect
10+
import openpyxl
11+
from openpyxl.styles import Font, Alignment, PatternFill
12+
from openpyxl.utils import get_column_letter
13+
import os
14+
15+
def get_stock_codes_and_names():
16+
url = "https://site2.sbisec.co.jp/ETGate/?OutSide=on&_ControlID=WPLETmgR001Control&_PageID=WPLETmgR001Mdtl20&_DataStoreID=DSWPLETmgR001Control&_ActionID=DefaultAID&getFlg=on&burl=search_market&cat1=market&cat2=none&dir=info&file=market_meigara_225.html"
17+
18+
response = requests.get(url)
19+
if response.status_code != 200:
20+
print(f"Failed to fetch the webpage. Status code: {response.status_code}")
21+
return []
22+
23+
soup = BeautifulSoup(response.content, 'html.parser')
24+
stock_table = soup.find('table', {'class': 'md-l-table-type01'})
25+
if stock_table is None:
26+
all_tables = soup.find_all('table')
27+
for table in all_tables:
28+
if table.find('tr'):
29+
stock_table = table
30+
break
31+
if stock_table is None:
32+
return []
33+
34+
stock_data = []
35+
for row in stock_table.find_all('tr')[1:]:
36+
cells = row.find_all('td')
37+
if cells:
38+
stock_code = cells[0].text.strip()
39+
company_name = cells[1].text.strip()
40+
stock_data.append((stock_code, company_name))
41+
return stock_data
42+
43+
def scrape_nikkei_news(stock_number):
44+
url = f"https://www.nikkei.com/nkd/company/news/?scode={stock_number}&ba=1"
45+
response = requests.get(url)
46+
soup = BeautifulSoup(response.content, 'html.parser')
47+
news_items = soup.find_all('a', href=lambda href: href and "/nkd/company/article/" in href)
48+
news_data = [{"title": item.text.strip(), "url": "https://www.nikkei.com" + item['href']} for item in news_items]
49+
return news_data
50+
51+
def scrape_yahoo_finance_news(stock_number):
52+
ticker = f"{stock_number}.T"
53+
url = f"https://finance.yahoo.co.jp/quote/{ticker}/news"
54+
response = requests.get(url)
55+
soup = BeautifulSoup(response.content, 'html.parser')
56+
news_items = soup.find_all('a', href=lambda href: href and "/news/" in href)
57+
news_data = []
58+
for item in news_items:
59+
title = item.text.strip()
60+
article_url = item['href']
61+
if not article_url.startswith('http'):
62+
article_url = "https://finance.yahoo.co.jp" + article_url
63+
news_data.append({"title": title, "url": article_url})
64+
return news_data
65+
66+
def analyze_sentiment(text, ja_tokenizer, ja_model, en_tokenizer, en_model):
67+
try:
68+
lang = detect(text)
69+
except:
70+
lang = 'ja' # Default to Japanese if detection fails
71+
72+
tokenizer = ja_tokenizer if lang == 'ja' else en_tokenizer
73+
model = ja_model if lang == 'ja' else en_model
74+
75+
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
76+
outputs = model(**inputs)
77+
sentiment_score = torch.softmax(outputs.logits, dim=1).tolist()[0]
78+
return sentiment_score[0] # Return the raw sentiment score
79+
80+
def sentiment_to_text(score):
81+
if score > 0.8:
82+
return "Very Negative"
83+
elif score > 0.6:
84+
return "Negative"
85+
elif score > 0.4:
86+
return "Neutral"
87+
elif score > 0.2:
88+
return "Positive"
89+
else:
90+
return "Very Positive"
91+
92+
def calculate_average_sentiment(sentiments):
93+
if not sentiments:
94+
return "Neutral"
95+
avg_sentiment = sum(sentiments) / len(sentiments)
96+
return sentiment_to_text(avg_sentiment)
97+
98+
def get_stock_data(stock_number):
99+
ticker = f"{stock_number}.T"
100+
end_date = datetime.now()
101+
start_date = end_date - timedelta(days=30)
102+
103+
try:
104+
df = yf.download(ticker, start=start_date, end=end_date)
105+
if df.empty:
106+
return None
107+
108+
df = df.reset_index()
109+
df['Date'] = pd.to_datetime(df['Date'])
110+
stock_data = [(row['Date'], row['Close']) for _, row in df.iterrows()]
111+
stock_data.sort(key=lambda x: x[0], reverse=True)
112+
return stock_data[:30]
113+
114+
except Exception as e:
115+
print(f"Error retrieving stock data: {e}")
116+
return None
117+
118+
def calculate_stock_trend(stock_data):
119+
if not stock_data or len(stock_data) < 2:
120+
return "No trend data"
121+
122+
first_price = stock_data[-1][1] # Oldest price
123+
last_price = stock_data[0][1] # Most recent price
124+
125+
percent_change = ((last_price - first_price) / first_price) * 100
126+
127+
if percent_change > 5:
128+
return "Strong Uptrend"
129+
elif percent_change > 2:
130+
return "Uptrend"
131+
elif percent_change < -5:
132+
return "Strong Downtrend"
133+
elif percent_change < -2:
134+
return "Downtrend"
135+
else:
136+
return "Neutral"
137+
138+
def get_action_recommendation(public_opinion, stock_trend, stock_price_data, purchase_price=None):
139+
if not stock_price_data:
140+
return "Insufficient data for recommendation"
141+
142+
opinion_score = {"Very Positive": 2, "Positive": 1, "Neutral": 0, "Negative": -1, "Very Negative": -2}
143+
trend_score = {"Strong Uptrend": 2, "Uptrend": 1, "Neutral": 0, "Downtrend": -1, "Strong Downtrend": -2}
144+
145+
total_score = opinion_score.get(public_opinion, 0) + trend_score.get(stock_trend, 0)
146+
147+
prices = [price for _, price in stock_price_data]
148+
current_price = prices[0]
149+
avg_price = np.mean(prices)
150+
std_dev = np.std(prices)
151+
152+
owns_stock = purchase_price is not None
153+
154+
if owns_stock:
155+
price_change = (current_price - purchase_price) / purchase_price * 100
156+
157+
if total_score > 0:
158+
action = "Hold"
159+
explanation = f"Positive outlook. You're currently up {price_change:.2f}%. Consider holding for potential further gains."
160+
elif total_score < 0:
161+
action = "Consider Selling"
162+
explanation = f"Negative outlook. You're currently {'up' if price_change > 0 else 'down'} {abs(price_change):.2f}%. Consider selling to {'lock in profits' if price_change > 0 else 'minimize losses'}."
163+
else:
164+
action = "Hold and Monitor"
165+
explanation = f"Mixed signals. You're currently {'up' if price_change > 0 else 'down'} {abs(price_change):.2f}%. Monitor the stock closely for changes in sentiment or market trends."
166+
167+
if price_change > 20:
168+
explanation += " However, with significant gains, consider taking partial profits."
169+
elif price_change < -20:
170+
explanation += " However, with significant losses, reassess your investment thesis."
171+
else:
172+
if total_score > 0:
173+
target_price = max(current_price * 0.99, avg_price - 0.5 * std_dev)
174+
action = f"Consider Buying (Target: ¥{target_price:.2f})"
175+
explanation = "Positive outlook. Consider buying near the suggested target price."
176+
elif total_score < 0:
177+
action = "Hold Off"
178+
explanation = "Negative outlook. It might be better to wait for a more favorable entry point."
179+
else:
180+
action = "Monitor"
181+
explanation = "Mixed signals. Monitor the stock for a clearer trend before making a decision."
182+
183+
return f"{action}\nExplanation: {explanation}"
184+
185+
def get_yahoo_finance_price(stock_number):
186+
url = f"https://finance.yahoo.co.jp/quote/{stock_number}.T"
187+
response = requests.get(url)
188+
soup = BeautifulSoup(response.content, 'html.parser')
189+
price_element = soup.find('span', class_='_3rXWJKZF')
190+
if price_element:
191+
price_text = price_element.text.replace(',', '')
192+
try:
193+
return int(float(price_text))
194+
except ValueError:
195+
print(f"Warning: Unable to convert price to integer for stock {stock_number}: {price_text}")
196+
return None
197+
return None
198+
199+
def create_excel_report(stock_analysis):
200+
today = datetime.now().date()
201+
directory = r"C:\Users\ka1t0\Documents\Python-Stock-Trade"
202+
filename = os.path.join(directory, "Nikkei225_Stock_Analysis.xlsx")
203+
204+
try:
205+
wb = openpyxl.load_workbook(filename)
206+
ws = wb.active
207+
last_column = ws.max_column
208+
except FileNotFoundError:
209+
wb = openpyxl.Workbook()
210+
ws = wb.active
211+
ws.title = "Stock Analysis"
212+
last_column = 1
213+
214+
# Set up header row (dates)
215+
ws.cell(row=1, column=last_column + 1, value=today)
216+
ws.cell(row=1, column=last_column + 1).font = Font(bold=True)
217+
ws.cell(row=1, column=last_column + 1).alignment = Alignment(horizontal='center', vertical='center')
218+
219+
# Set up row headers if it's a new file
220+
if last_column == 1:
221+
row_headers = [
222+
"Company Name",
223+
"Stock Code",
224+
"Stock Price",
225+
"Previous Day Stock",
226+
"Compared to day before",
227+
"Nikkei Perception",
228+
"Yahoo Finance Perception",
229+
"Overall Perception",
230+
"Action"
231+
]
232+
for row, header in enumerate(row_headers, start=2):
233+
cell = ws.cell(row=row, column=1, value=header)
234+
cell.font = Font(bold=True)
235+
cell.alignment = Alignment(horizontal='left', vertical='center')
236+
ws.row_dimensions[row].height = 20
237+
238+
def color_sentiment(cell, sentiment):
239+
if sentiment == "Positive" or sentiment == "Very Positive":
240+
cell.fill = PatternFill(start_color="00FF00", end_color="00FF00", fill_type="solid")
241+
elif sentiment == "Negative" or sentiment == "Very Negative":
242+
cell.fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
243+
elif sentiment == "Neutral":
244+
cell.fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
245+
246+
# Populate data
247+
for row, stock in enumerate(stock_analysis, start=2):
248+
if last_column == 1:
249+
ws.cell(row=row, column=1, value=stock['company_name'])
250+
ws.cell(row=row, column=2, value=stock['stock_number'])
251+
252+
current_price = stock['current_stock_price']
253+
previous_price = stock['previous_stock_price']
254+
255+
if current_price is not None and previous_price is not None:
256+
price_change_percent = ((current_price - previous_price) / previous_price) * 100
257+
else:
258+
price_change_percent = "N/A"
259+
260+
ws.cell(row=row, column=last_column + 1, value=current_price if current_price is not None else "N/A")
261+
ws.cell(row=row, column=last_column + 2, value=previous_price if previous_price is not None else "N/A")
262+
263+
change_cell = ws.cell(row=row, column=last_column + 3, value=f"{price_change_percent:.2f}%" if price_change_percent != "N/A" else price_change_percent)
264+
if price_change_percent != "N/A":
265+
if price_change_percent > 0:
266+
change_cell.fill = PatternFill(start_color="00FF00", end_color="00FF00", fill_type="solid")
267+
elif price_change_percent < 0:
268+
change_cell.fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
269+
else:
270+
change_cell.fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
271+
272+
nikkei_cell = ws.cell(row=row, column=last_column + 4, value=stock['nikkei_sentiment'])
273+
color_sentiment(nikkei_cell, stock['nikkei_sentiment'])
274+
275+
yahoo_cell = ws.cell(row=row, column=last_column + 5, value=stock['yahoo_sentiment'])
276+
color_sentiment(yahoo_cell, stock['yahoo_sentiment'])
277+
278+
overall_cell = ws.cell(row=row, column=last_column + 6, value=stock['overall_sentiment'])
279+
color_sentiment(overall_cell, stock['overall_sentiment'])
280+
281+
action_cell = ws.cell(row=row, column=last_column + 7, value=stock['action_recommendation'].split('\n')[0]) # Only the action, not the explanation
282+
if "Buy" in action_cell.value:
283+
action_cell.fill = PatternFill(start_color="00FF00", end_color="00FF00", fill_type="solid")
284+
285+
# Adjust column widths
286+
for col in range(1, ws.max_column + 1):
287+
ws.column_dimensions[get_column_letter(col)].width = 15
288+
289+
# Save the workbook
290+
wb.save(filename)
291+
print(f"Excel report updated and saved as {filename}")
292+
293+
def main():
294+
stock_data = get_stock_codes_and_names()
295+
if not stock_data:
296+
print("Failed to retrieve stock data. Please check your internet connection and try again.")
297+
return
298+
299+
total_stocks = len(stock_data)
300+
print(f"Total stocks to process: {total_stocks}")
301+
302+
ja_tokenizer = AutoTokenizer.from_pretrained("jarvisx17/japanese-sentiment-analysis")
303+
ja_model = AutoModelForSequenceClassification.from_pretrained("jarvisx17/japanese-sentiment-analysis")
304+
305+
en_tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")
306+
en_model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")
307+
308+
stock_analysis = []
309+
310+
for index, (stock_number, company_name) in enumerate(stock_data, 1):
311+
try:
312+
print(f"Processing: {company_name} ({stock_number}) - {index}/{total_stocks}", end='\r')
313+
314+
nikkei_news_data = scrape_nikkei_news(stock_number)
315+
yahoo_finance_news_data = scrape_yahoo_finance_news(stock_number)
316+
317+
nikkei_sentiments = [analyze_sentiment(news['title'], ja_tokenizer, ja_model, en_tokenizer, en_model) for news in nikkei_news_data]
318+
yahoo_finance_sentiments = [analyze_sentiment(news['title'], ja_tokenizer, ja_model, en_tokenizer, en_model) for news in yahoo_finance_news_data]
319+
320+
nikkei_overall_sentiment = calculate_average_sentiment(nikkei_sentiments)
321+
yahoo_finance_overall_sentiment = calculate_average_sentiment(yahoo_finance_sentiments)
322+
323+
overall_sentiment_value = (sum(nikkei_sentiments) + sum(yahoo_finance_sentiments)) / (len(nikkei_sentiments) + len(yahoo_finance_sentiments)) if nikkei_sentiments or yahoo_finance_sentiments else 0.5
324+
overall_sentiment = sentiment_to_text(overall_sentiment_value)
325+
326+
stock_price_data = get_stock_data(stock_number)
327+
current_stock_price = get_yahoo_finance_price(stock_number)
328+
previous_stock_price = stock_price_data[1][1] if len(stock_price_data) > 1 else None
329+
stock_trend = calculate_stock_trend(stock_price_data)
330+
331+
action_recommendation = get_action_recommendation(overall_sentiment, stock_trend, stock_price_data)
332+
333+
stock_analysis.append({
334+
'stock_number': stock_number,
335+
'company_name': company_name,
336+
'current_stock_price': current_stock_price,
337+
'previous_stock_price': previous_stock_price,
338+
'nikkei_sentiment': nikkei_overall_sentiment,
339+
'yahoo_sentiment': yahoo_finance_overall_sentiment,
340+
'overall_sentiment': overall_sentiment,
341+
'stock_trend': stock_trend,
342+
'action_recommendation': action_recommendation,
343+
'stock_price_data': stock_price_data
344+
})
345+
346+
except Exception as e:
347+
print(f"\nError processing {company_name} ({stock_number}): {str(e)}")
348+
349+
print("\nAll stocks processed.")
350+
create_excel_report(stock_analysis)
351+
352+
if __name__ == '__main__':
353+
main()

0 commit comments

Comments
 (0)