จัดการข้อมูลอย่าง Quants [Part 1: ดึงข้อมูลหุ้น S&P500 ทั้ง 500 ตัว!]

บทความชุดนี้เราจะมาเอาใจสายลงทุนกัน ด้วยบทความชุด “จัดการข้อมูลอย่าง Quants” ซึ่งจะประกอบไปด้วย 3 บทความด้วยกัน ดังนี้

  1. จัดการข้อมูลอย่าง Quants [Part 1: ดึงข้อมูลหุ้น S&P500 ทั้ง 500 ตัว]
  2. หลากหลายวิธีกับการจัดการกับ “Missing Value”
  3. จัดการข้อมูลอย่าง Quant [Part2: จัดการ Missing Value และ คำนวณ Statistics สำหรับข้อมูลทั้งตลาด]

บทความทั้งหมดจะเป็น Tutorial สำหรับผู้ที่สนใจการดึงข้อมูลหุ้นเพื่อการลงทุนแบบไม่เสียค่าใช้จ่าย! ซึ่งจะสอนตั้งแต่การหาข้อมูล การเขียนโปรแกรมทุกขั้นตอนจนกระทั่งได้ข้อมูล .CSV พร้อมใช้งาน ไม่เพียงแค่นี้ นอกจากสอนดึงข้อมูลแล้ว เรายังสอนการจัดการกับข้อมูลเบื้องต้น การทำความสะอาดข้อมูล (Data Cleaning) เพื่อเตรียมข้อมูลให้พร้อมสำหรับการวิเคราะห์ในขั้นตอนต่อไปอีกด้วย

ไปเริ่มกันเลยครับ อ่านไปด้วย Coding ไปด้วยได้เลยครับ

ดึงข้อมูลหุ้นทั้ง 500 ตัวจาก S&P500

Import ไลบารี่ที่จำเป็นกันก่อน

import pandas as pd
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')

เริ่มด้วยไปที่ Wikipedia ที่ List รายชื่อหุ้นในกลุ่ม S&P500 ไว้ทั้งหมด

df_html = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
df_html

เราใช้ฟังก์ชั่น pd.read_html เพื่ออ่านหน้าเพจจากเว็ปไซต์ https://en.wikipedia.org/wiki/List_of_S%26P_500_companies เราจะได้ list ที่ใส่ ข้อมูลในเว็ปไซต์หลายตัว แต่เราสนใจเฉพาะตารางที่บรรจุชื่อ

df = df_html
df = list(df[0]["Symbol"])
df

ที่ตารางแรกเราสนใจ columns ที่ชื่อ Symbol ที่จะให้ชื่อของหุ้นทุกตัว จากนั้นเราทำด้านบนให้เป็นฟังก์ชั่น

def get_sp500_tickers():
    df_html = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
    df = df_html[0]
    return list(df["Symbol"])
tickers = get_sp500_tickers()

เรียกใช้ฟังก์ชั่นเก็บชื่อหุ้นใส่ตัวแปรชื่อ tickers

symbols = tickers[::15]
symbols

เนื่องจากถ้าเรารันทั้ง 500 ตัวอาจจะเสียเวลาไปบ้าง เราจะใช้คำสั่ง tickers[::10] ซึ่งเราอาจจะไม่คุ้นกันนัก แต่มันมีความหมายว่า ให้เรียกข้อมูล 1 ตัว ทุกๆ 15 ตัว ไปจนหมดทั้งความยาวของ tickers

len(symbols), len(tickers)

ลองดูความยาวมัน ข้อมูลหุ้นที่เราทำการ sample มาทุกๆ 15 ตัวจะมีขนาด 24 ตัว จาก 503 ตัวของ S&P500 ตาม

เริ่มดึงข้อมูลหุ้นด้วย YahooFinance

 yf.Ticker(symbols[0]).history(period="10y")

เราใช้คำสั่ง .Ticker เพื่อดึงข้อมูลหุ้นมา เป็นระยะเวลาย้อนหลัง 10 ปี จากนั้นเรา

ohlcvs = {}
for symbol in symbols:
        symbol_df = yf.Ticker(symbol).history(period="10y") 
        ohlcvs[symbol] = symbol_df[["Open", "High", "Low", "Close", "Volume"]].rename(
                                    columns={"Open": "open", "High": "high", "Low": "low",
                                                "Close": "close","Volume": "volume"} )
        print(symbol)

เนื่องจาก เราไม่ได้ใช้งาน columns Dividends และ Stock Splits เราเลยตัดออก แล้วก็ชื่อ อันนี้เป็นแค่สไตล์ สำหรับผมชอบตัวพิมพ์เล็ก ผมเลยใช้คำสั่ง rename columns เป็นตัวเล็กทั้งหมด

เราเก็บหุ้นทุกตัวไว้ใน list ที่ชื่อ ohlcvs โดยมี key ของ list เป็นชื่อของหุ้นตัวนั้นๆ

df = pd.DataFrame(index=ohlcvs["MMM"].index)
df.index.name = "date"
sample_tickers = list(ohlcvs.keys())

เราจะยืม index ของ MMM หรือ 3M มาเป็น index ของ dataframe ชุดใหญ่ของเรา เราเก็บชื่อหุ้นที่เรา sample มาไว้ใช้ในอนาคตด้วย

Map ชื่อหุ้นและ OHLC

l = []
for i in (sample_tickers):
    l.append(str(i)+ " open")
l

เนื่องจากเราจะเก็บ open high low close ของหุ้นหมดไว้ใน dataframe เดียวกันฉะนั้นเราจึงต้องการชื่อที่แตกต่างกันสำหรับหุ้นแต่ละตัว โดยจะเริ่มจากการใช้ for loop แต่ทำแบบนี้มันเสียเวลา python มีฟังก์ชั่นที่ดีกว่านี้ให้เราใช้แล้วคือ map

list(map(lambda x: "{} {}".format("MMM", x), s_df.columns)) 

ทดลอง map ชื่อหุ้น MMM กับ columns ของมัน มันจะสะดวกกว่าวน loop หลายๆรอบ

for s in sample_tickers:
    s_df = ohlcvs[s]
    cols = list(map(lambda x: "{} {}".format(s, x), s_df.columns)) 
    df[cols] = s_df
df

เก็บข้อมูลไว้ใน dataframe เดียวกัน

def get_sp500_tickers():
    df_html = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
    df = df_html[0]
    return list(df["Symbol"])

def get_sp500_df():
    symbols = get_sp500_tickers()
    symbols = symbols[::15]
    ohlcvs = {}
    for symbol in symbols:
            symbol_df = yf.Ticker(symbol).history(period="10y") 
            ohlcvs[symbol] = symbol_df[["Open", "High", "Low", "Close", "Volume"]].rename(
                                    columns={"Open": "open", "High": "high", "Low": "low",
                                                "Close": "close","Volume": "volume"} )
    

    df = pd.DataFrame(index=ohlcvs["MMM"].index)
    df.index.name = "date"
    sample_tickers = list(ohlcvs.keys())

    for s in sample_tickers:
        s_df = ohlcvs[s]
        cols = list(map(lambda x: "{} {}".format(s, x), s_df.columns)) 
        df[cols] = s_df

    return df, sample_tickers

df, tickers = get_sp500_df()

ทำมันเป็นฟักชั่น และลองเรียกใช้งาน จากนั้นเราจะ

for ticker in tickers:
    plt.plot(df["{} close".format(ticker)], label=ticker)
    
plt.title("sample of S&P500 close price")    
plt.legend()
plt.show()
df = df.tz_localize(None)
df.to_excel("sp500_data.xlsx")

เก็บข้อมูลในรูปแบบของ excel ไฟล์ เท่านี้ก็สามารถเก็บข้อมูลไว้เพื่อใช้งานในอนาคตได้แล้วครับ

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s