บทความชุดนี้เราจะมาเอาใจสายลงทุนกัน ด้วยบทความชุด “จัดการข้อมูลอย่าง Quants” ซึ่งจะประกอบไปด้วย 3 บทความด้วยกัน ดังนี้
- จัดการข้อมูลอย่าง Quants [Part 1: ดึงข้อมูลหุ้น S&P500 ทั้ง 500 ตัว]
- หลากหลายวิธีกับการจัดการกับ “Missing Value”
- จัดการข้อมูลอย่าง 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 ไฟล์ เท่านี้ก็สามารถเก็บข้อมูลไว้เพื่อใช้งานในอนาคตได้แล้วครับ