나만의 배당주 사이트 만들기) 2-3. 개발 단계 - 사이트에 적용하기(장고)

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분은 걸리는 거 같다 ..

끊임없이 돌아가는 동글뱅이...

 

힘겨워보이는 터미널 ㅜㅋㅋㅋㅋㅋ

 

 

내가 원하는 정보대로 뜨긴했다!!! 

 

진짜 ... 감격 ㅜㅜㅜㅜ 휴

 

 

 

 

엄 근데.... 과부하를 줄일 방법을 찾아봐야겠다

데이터를 불러오느라 ... 굉장히 많은 시간이 걸리는 건 알겠지만

모르는 사람이 사용한다면 페이지가 고장난 줄 알것이다 !

 

실사용을 위해 과부하를 줄여보자! 

는 다음글~~~