2025. 1. 16. 13:00ㆍ프로젝트
이 전 글들이 궁금하다면 ?
0. 사이트를 만드려는 이유
https://hsjoo126.tistory.com/80
1-1. 프로젝트 가능성 보기
https://hsjoo126.tistory.com/81
pandas 와 jupyter 이용해서 테스트해보기
https://hsjoo126.tistory.com/82
1-2. 기획 단계 - 디자인, 와이어 프레임, ERD 등
https://hsjoo126.tistory.com/83
2. 개발 단계 - 계획짜기, 구현해보기
https://hsjoo126.tistory.com/84
2-1. 개발 단계 - 배당지불일, 시장별 티커리스트 구하기
https://hsjoo126.tistory.com/85
2-2. 개발 단계 - 코드 정리
https://hsjoo126.tistory.com/86
이전 글에서 내가 원하는 데이터를 뽑아올 수 있게끔 가볍게 코드를 적어봤고,
이제 장고에 적용해 사이트로 띄울 차례이다!
GPT 랑 티키타카
우리 사이트에서 제일 중요한 건 주식 정보를 깔끔하게 제공하는 것이다.
나는 처음 보는 사람이어도 우리 사이트에 들어왔을 때 아~ 이건 이거구나! 하는 걸 원했다.
그래서 gpt 한테 내가 항목별로 구현한 코드를 던져주고 테이블 형태로 사이트를 만들어달라고 했다.
GPT 는 이런식으로 코드를 짜서 주었다.
# views.py
from django.shortcuts import render
import yfinance as yf
import FinanceDataReader as fdr
def stock_info(request):
# 나스닥(NASDAQ) 주식 정보 가져오기
nasdaq_stocks = fdr.StockListing('NASDAQ')
# Symbol 열만 추출
symbols = nasdaq_stocks['Symbol']
# 주식 정보 가져올 티커 리스트 설정 (여기서는 예시로 일부 티커만 사용)
tickers = ['AAPL', 'MSFT', 'GOOG', 'AMZN', 'TSLA']
stock_data = []
for ticker_symbol in tickers:
ticker = yf.Ticker(ticker_symbol)
# 현재 주가
current_price = ticker.info['currentPrice']
# 마지막 배당금
dividends = ticker.dividends
last_dividend_value = dividends.iloc[-1] if not dividends.empty else None
# 마지막 배당일
info = ticker.calendar
dividend_date = info.loc['Dividends', 0] if 'Dividends' in info.index else None
# 배당수익률
yield_value = ticker.info.get('dividendYield', 0) * 100
# 시가총액
market_cap = ticker.info['marketCap']
# 데이터 추가
stock_data.append({
'ticker': ticker_symbol,
'current_price': current_price,
'last_dividend': last_dividend_value,
'dividend_date': dividend_date,
'dividend_yield': round(yield_value, 2),
'market_cap': "{:,}".format(market_cap),
})
return render(request, 'stock_info.html', {'stock_data': stock_data})
근데... 홈페이지에 오류가 떴다.
AttributeError: 'dict' object has no attribute 'index'
GPT한테 물어보니 이런 답변이 나왔다.
AttributeError: 'dict' object has no attribute 'index' 오류는
ticker.calendar에서 발생한 것입니다.
ticker.calendar는 pandas DataFrame 형식으로 반환됩니다.
그런데 Dividends가 DataFrame에 존재하지 않을 때 발생할 수 있습니다.
배당일에서 문제가 생긴 거 같았다.
그래서 배당일이 없을 경우와 인덱스 형식으로 존재할 경우 값을 내도록 예외처리를 해주었다.
# 마지막 배당일
info = ticker.calendar
dividend_date = None
if 'Dividends' in info.index:
dividend_date = info.loc['Dividends', 0]
근데 ? 다시 돌렸는데, 같은 오류가 나왔다.
알고보니 ticker.calendar 가 dict 형식일 때도 있었고, dataframe 형식일 때도 있었다.
그 두 경우 다 처리해주어야했다.
그래서
- 배당일이 없는 경우
- dict 형식일 경우
- dataframe 형식일 경우
모든 경우를 코드에 적어주었다
# 마지막 배당일
info = ticker.calendar
dividend_date = None
# info가 dict 형식인 경우, Dividends 항목이 존재하는지 확인
if isinstance(info, dict) and 'Dividends' in info:
dividend_date = info['Dividends']
# info가 DataFrame 형식인 경우, Dividends 항목이 존재하는지 확인
elif isinstance(info, pd.DataFrame) and 'Dividends' in info.index:
dividend_date = info.loc['Dividends', 0]
그런데도...?
홈페이지에 아무것도 안 떠서 ... 어떡하지 ..? ㅋㅋㅋㅋ ㅠㅠㅠ 하다가
프린트를 다 찍어보았다
def middle(request):
# 나스닥(NASDAQ) 주식 정보 가져오기
nasdaq_stocks = fdr.StockListing('NASDAQ')
# Symbol 열만 추출
symbols = nasdaq_stocks['Symbol']
# 주식 정보 가져올 티커 리스트 설정 (여기서는 예시로 일부 티커만 사용)
tickers = ['AAPL']
stock_data = []
for ticker_symbol in tickers:
ticker = yf.Ticker(ticker_symbol)
print('-------------ticker--------------')
# 현재 주가
current_price = ticker.info['currentPrice']
print('-------------currentPrice--------------')
# 마지막 배당금
dividends = ticker.dividends
last_dividend_value = dividends.iloc[-1] if not dividends.empty else None
print('-------------last--------------')
# 마지막 배당일
info = ticker.calendar
dividend_date = None
print('-------------calendar--------------')
# info가 dict 형식인 경우, Dividends 항목이 존재하는지 확인
if isinstance(info, dict) and 'Dividends' in info:
dividend_date = info['Dividends']
print('-------------calendarDict--------------')
# info가 DataFrame 형식인 경우, Dividends 항목이 존재하는지 확인
elif isinstance(info, pd.DataFrame) and 'Dividends' in info.index:
dividend_date = info.loc['Dividends', 0]
print('-------------calendarfinal--------------')
# 배당수익률
yield_value = ticker.info.get('dividendYield', 0) * 100
print('-------------yield--------------')
# 시가총액
market_cap = ticker.info['marketCap']
print('-------------Cap--------------')
# 데이터 추가
stock_data.append({
'ticker': ticker_symbol,
'current_price': current_price,
'last_dividend': last_dividend_value,
'dividend_date': dividend_date,
'dividend_yield': round(yield_value, 2),
'market_cap': "{:,}".format(market_cap),
})
print('-------------append--------------')
return render(request, "stocks/middle.html", {"stocks_data" : stock_data})
엄.... 프린트는 잘 나오는데 홈페이지엔 여전히 안 떠서 ... 뭐가 문제지? 하면서 보는데
배당일이.. 내가 GPT 한테 준 코드가 아니었다!
크흡 ..... GPT를 너무 믿은... 내 잘못이었달까..
def middle(request):
# 나스닥(NASDAQ) 주식 정보 가져오기
nasdaq_stocks = fdr.StockListing('NASDAQ')
# Symbol 열만 추출
symbols = nasdaq_stocks['Symbol']
# 주식 정보 가져올 티커 리스트 설정 (여기서는 예시로 일부 티커만 사용)
tickers = ['AAPL']
stock_data = []
for ticker_symbol in tickers:
ticker = yf.Ticker(ticker_symbol)
print(f'-------------ticker: {ticker_symbol}--------------')
# 현재 주가
current_price = ticker.info.get('currentPrice', '정보 없음')
print(f'-------------currentPrice: {current_price}--------------')
# 마지막 배당금
dividends = ticker.dividends
last_dividend_value = dividends.iloc[-1] if not dividends.empty else None
print(f'-------------last_dividend_value: {last_dividend_value}--------------')
# 마지막 배당일
info = ticker.calendar
print(f'-------------calendar: {info}--------------')
dividend_date = None
# info가 dict 형식인 경우, Dividend Date 항목을 확인
if isinstance(info, dict) and 'Dividend Date' in info:
dividend_date = info['Dividend Date']
print('-------------calendarDict: Found Dividend Date--------------')
# info가 DataFrame 형식인 경우, Dividends 항목이 존재하는지 확인
elif isinstance(info, pd.DataFrame) and 'Dividends' in info.index:
dividend_date = info.loc['Dividends', 0]
print('-------------calendarDataFrame: Found Dividends--------------')
else:
print('-------------No Dividends Data Found--------------')
# 배당수익률
yield_value = ticker.info.get('dividendYield', 0) * 100
print(f'-------------dividendYield: {yield_value}--------------')
# 시가총액
market_cap = ticker.info.get('marketCap', '정보 없음')
print(f'-------------market_cap: {market_cap}--------------')
# 데이터 추가
stock_data.append({
'ticker': ticker_symbol,
'current_price': current_price,
'last_dividend': last_dividend_value,
'dividend_date': dividend_date,
'dividend_yield': round(yield_value, 2),
'market_cap': "{:,}".format(market_cap) if isinstance(market_cap, int) else market_cap,
})
print('-------------append to stock_data--------------')
return render(request, "stocks/middle.html", {"stocks_data": stock_data})
수정된 코드로 하니 출력로그가 잘 떴다!
#결과 코드
100%|██████████████████████████████████████████████████████████████████████████████████████| 3640/3640 [00:05<00:00, 676.63it/s]
-------------ticker: AAPL--------------
-------------currentPrice: 242.7--------------
-------------last_dividend_value: 0.25--------------
-------------calendar: {'Dividend Date': datetime.date(2024, 11, 14), 'Ex-Dividend Date': datetime.date(2024, 11, 8), 'Earnings Date': [datetime.date(2025, 1, 30)], 'Earnings High': 2.5, 'Earnings Low': 2.19, 'Earnings Average': 2.35371, 'Revenue High': 129887000000, 'Revenue Low': 119563000000, 'Revenue Average': 124217662300}--------------
-------------calendarDict: Found Dividend Date 2024-11-14--------------
-------------dividendYield: 0.41000000000000003--------------
-------------market_cap: 3668604616704--------------
-------------append to stock_data--------------
[10/Jan/2025 13:47:28] "GET /stocks/middle/ HTTP/1.1" 200 1016
아무튼, 위에 코드에서 실험용으로 AAPL 만 잘 뜨는지 확인했던 건데,
이제 실험은 끝났으니까,
- 나스닥에서 티커리스트 바로 불러오기
- 배당률 4~7%애들만 불러오기
를 코드에 적용했다.
근데 나스닥에 있는 애들을.. 다 불러올 순 없으니까 (너무 많아서!)
일단 100개 정도만 잘라서 불러오게끔했다.
최종 코드
def middle(request):
# 나스닥(NASDAQ) 주식 정보 가져오기
nasdaq_stocks = fdr.StockListing('NASDAQ')
# Symbol 열만 추출
symbols = nasdaq_stocks['Symbol']
tickers = symbols[:100]
stock_data = []
for ticker_symbol in tickers:
ticker = yf.Ticker(ticker_symbol)
# 현재 주가
current_price = ticker.info.get('currentPrice', '정보 없음')
# 마지막 배당금
dividends = ticker.dividends
last_dividend_value = dividends.iloc[-1] if not dividends.empty else None
# 마지막 배당일
info = ticker.calendar
dividend_date = None
# info가 dict 형식인 경우, Dividend Date 항목을 확인
if isinstance(info, dict) and 'Dividend Date' in info:
dividend_date = info['Dividend Date']
# info가 DataFrame 형식인 경우, Dividends 항목이 존재하는지 확인
elif isinstance(info, pd.DataFrame) and 'Dividends' in info.index:
dividend_date = info.loc['Dividends', 0]
# 배당수익률
yield_value = ticker.info.get('dividendYield', 0) * 100
# 시가총액
market_cap = ticker.info.get('marketCap', '정보 없음')
if 4 <= yield_value <= 7:
stock_data.append({
'ticker': ticker_symbol,
'current_price': current_price,
'last_dividend': last_dividend_value,
'dividend_date': dividend_date,
'dividend_yield': round(yield_value, 2),
'market_cap': "{:,}".format(market_cap) if isinstance(market_cap, int) else market_cap,
})
return render(request, "stocks/middle.html", {"stocks_data": stock_data})
html 도 이런식으로 작성했다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>주식 정보</title>
<style>
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
</style>
</head>
<body>
<h1>주식 정보</h1>
<table>
<thead>
<tr>
<th>Ticker</th>
<th>현재 주가</th>
<th>마지막 배당금</th>
<th>배당일</th>
<th>배당수익률</th>
<th>시가총액</th>
</tr>
</thead>
<tbody>
{% for stock in stocks_data %}
<tr>
<td>{{ stock.ticker }}</td>
<td>{{ stock.current_price }}</td>
<td>{{ stock.last_dividend }}</td>
<td>{{ stock.dividend_date }}</td>
<td>{{ stock.dividend_yield }}%</td>
<td>{{ stock.market_cap }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
와우!! 근데 진짜 ㅋㅋㅋㅋㅋ 너무너무 오래걸린다...
체감상 10분은 걸리는 거 같다 ..
내가 원하는 정보대로 뜨긴했다!!!
진짜 ... 감격 ㅜㅜㅜㅜ 휴
엄 근데.... 과부하를 줄일 방법을 찾아봐야겠다
데이터를 불러오느라 ... 굉장히 많은 시간이 걸리는 건 알겠지만
모르는 사람이 사용한다면 페이지가 고장난 줄 알것이다 !
실사용을 위해 과부하를 줄여보자!
는 다음글~~~