將非結構性的新聞資料結合應用在財務上,最直覺的方法之一就是找出個股新聞與股價之間的關係了。在這之前,我們必須先知道哪些是個股新聞、哪些不是、哪些甚至是多股新聞,因此我們需要先處理一下資料。
# 匯入上市公司清單(記得將檔案放在同一個目錄wd下)
tw_stock <- readRDS("tw_stock.rds")
# 匯入新聞資料(檔案名稱、位址會因人而異)
news <- readRDS("MoneyDJ.rds")
# 這邊我們試著撰寫一個判定單篇個股新聞的函數
TM_getsymbol <- function(x, tw_stock) {
# 建立清單向量
symbol_list <- tw_stock$symbol
name_list <- tw_stock$cname
# 找出文章x中符合的個股名稱,回傳清單中第幾個股票有符合
name_match_no <- str_detect(x, name_list) %>% which %>% unique
if(length(name_match_no) != 0) {
# 類似vlookup的做法,先找到符合的名稱,再挑出對應的股票代碼,並合併在一起
name_match <- str_c(name_list[name_match_no], collapse = ",")
symbol_match <- str_c(symbol_list[name_match_no], collapse = ",")
} else {
name_match <- NA; symbol_match <- NA
}
output <- c(name_match, symbol_match)
return(output)
}
# 以迴圈方式執行判斷式,這邊以3,000篇文章作為測試
news[,c("cname", "symbol")] <- NA
for (i in 1:3000) {
news[i, c("cname", "symbol")] <- TM_getsymbol(news$content[i], tw_stock)
}
# 挑出個股新聞
news_stock <- subset(news[1:3000,], !is.na(symbol))
# 檢視前一篇文章的結果
head(news_stock, 1L)
## date title
## 3 2019-05-09 《陸股》滬指量縮收跌1.48%,向下測試年線支撐
## content
## 3 大陸滬深兩市今(9)日早盤開低,盤中晶片概念股走強,帶動創業板震盪拉升,惟受釀酒等白馬股持續下殺影響,滬指與深成指跌逾1.5%,創業板亦拉回至平盤下。午後,養雞、農業種植等類股雖然輪流拉升,但仍難敵市場整體頹勢,同時中美今日舉行第11輪貿易協商,市場觀望情緒濃厚,成交量續見萎縮;滬指終場收跌1.48%,連兩日下跌,續寫2月22日(2804.23點)以來新低,退守年線區。上證指數今日收於2850.95點,下跌42.81點或1.48%;深證成指收於8877.31點,下跌125.22點或1.39%;創業板指數收於1469.48點,下跌12.39點或0.84%。在成交量方面,滬市今成交1,974億元(人民幣,下同),深市成交2,366億元,兩市今共成交4,340億元,相較前一交易日量縮約11%,連三日萎縮。綜觀今日陸股盤面表現,飼料加工、漁牧、農林、航太國防、公共交通股漲幅居前,飲料、釀酒、食品加工、玻璃製造股則跌幅居前,保險股跌逾2%,銀行、黃金股跌逾1%。概念股中,生態農業、雲端運算軟體、人造肉、網路安全、全息手機、生豬等概念股見拉抬,工業大麻、醫藥、自貿港、人腦工程等概念股表現疲弱。港股方面,恆生指數今日開低走低,失守29000點,蘋果概念股、生物科技、航空股、陸資券商股跌幅居前,藍籌股幾近全面下跌;午後,恆指維持在28460點附近震盪,隨後跌勢再度擴大,最低見28281.98點、跌幅近2.5%,終場則收於28311.07點,下跌692.13點或2.39%。在香港上市台資股中,富智康集團跌8.33%,陽光能源跌逾3%,裕元集團、康師傅控股跌逾2%,跌逾1%;統一企業中國昨日大漲12.64%、今日收跌0.49%;九興控股則逆勢漲逾2%,凱普松國際漲逾1%。MoneyDJ 新聞 2019-05-09 16:43:52 記者新聞中心 報導
## cname symbol
## 3 統一,農林 1216,2913
判斷完之後,我們以台積電(2330)為範例,試試看能不能抓出台積電的新聞。str_detect()可以幫助我們判斷股票代碼欄位有2330的新聞,這個函數會回傳邏輯值(TRUE/FALSE),只要配合which()就能將news_stock物件中包含台積電新聞的列(row)的位置抓出來。擷取出來後的台積電新聞剩下130筆,代表有130篇文章中有提到台積電。
附帶一提,我所撰寫判斷個股新聞的函數其實相對簡陋,會有誤認的現象發生,例如,大量(3167)、新興(2605)等,股票名稱跟日常用詞高度相似或是重疊,可能會將“新興市場如何如何”的句子誤判成有提到新興(2605)的新聞。這類型的問題在文字探勘中很常碰到,也許需要更精確、嚴格的機制來判斷,但這邊不特別贅述該怎麼解決。
news_2330 <- news_stock[which(str_detect(news_stock$symbol, "2330")),]
print(nrow(news_2330))
## [1] 130
接著我們進一步來量化新聞資料,最簡單的做法就是計算文章的數量。進階一點就是透過進一步斷詞來衡量內容的情緒性字眼,量化出情緒指標並觀差它與股價之間的關係。我們先從最簡單的文章數量開始。
# 運用dplyr套件的功能,計算每日的文章數,並將日期轉換成日期格式
news_2330_N <- news_2330 %>%
group_by(date) %>%
summarise(N = length(content)) %>%
mutate(date = ymd(date))
# 檢查樣本時間
head(news_2330_N$date, n = 2L)
## [1] "2019-01-17" "2019-01-18"
tail(news_2330_N$date, n = 2L)
## [1] "2019-05-08" "2019-05-09"
# 利用highcharter套件,觀察資料pattern
library(highcharter)
highchart() %>%
hc_xAxis(categories = news_2330_N$date) %>%
hc_add_series(data = news_2330_N$N, type = "column", name = "Article Frequency") %>%
hc_add_theme(hc_theme_ft())
***
最後一步就是將台積電的股價與文章數量的變數做合併了,大部分的部落格以及教學都會推薦使用quantmod套件,因為它已經發展幾年了而且使用者也很多,但近年來由於資料來源如yahoo finance、google finance等出現API改版等問題,quantmod的穩定度就受影響了。
因此我推薦一個比較新且整合性高的tidyquant套件,這個套件 整合了如quantmod、PerformanceAnalytics等套件,非常實用,更多使用方法可以參照這裡。
library(tidyquant)
stock_2330 <- tq_get("2330.TW", get = "stock.prices", from = "2019-01-17")
# 選擇成交量以及調整後股價
stock_2330 <- select(stock_2330, c(date, volume, adjusted))
# 合併兩個變數,由於股價資料的日期較為齊全,以date來合併,但保留x端的資料
merged_2330 <- merge.data.frame(stock_2330, news_2330_N, by = "date", all.x = TRUE)
# 由於文章資料的日期不齊全,合併後N變數會有遺漏值(NA),補上0
merged_2330[which(is.na(merged_2330$N)),"N"] <- 0
highcharter是我非常推薦的套件之一,彈性程度很高,而且也十分簡潔漂亮。這個套件其實主要是由網頁開發者來使用,主要以JavaScript來編寫。但是官方推出了R的API,讓我們也能用R編寫互動式圖表,日後會再針對這個套件做更詳細的介紹。
# 1. 文章數量 vs. 成交量
highchart() %>%
hc_title(text = list("文章數量 vs. 成交量")) %>%
hc_xAxis(categories = merged_2330$date) %>%
hc_yAxis_multiples(list(title = list(text = "article frequency")),
list(title = list(text = "volume"), opposite = TRUE)) %>%
hc_add_series(data = merged_2330$N, type = "column", name = "Article Frequency", yAxis = 0) %>%
hc_add_series(data = merged_2330$volume, type = "column", name = "Volume", yAxis = 1) %>%
hc_add_theme(hc_theme_ft())
# 2. 文章數量 vs. 股價
highchart() %>%
hc_title(text = list("文章數量 vs. 股價")) %>%
hc_xAxis(categories = merged_2330$date) %>%
hc_yAxis_multiples(list(title = list(text = "article frequency")),
list(title = list(text = "volume"), opposite = TRUE)) %>%
hc_add_series(data = merged_2330$N, type = "line", name = "Article Frequency", yAxis = 0) %>%
hc_add_series(data = merged_2330$adjusted, type = "line", name = "Stock Price", yAxis = 1) %>%
hc_add_theme(hc_theme_ft())
很明顯的這不足以解釋成交量或是股價,若以統計的角度來看,將成交量以及股價作為應變數(Y),以文章數量作為自變數(X),會有時間序列的趨勢問題,其實還需要進一步去處理資料。學術方面其實已經在這方面有不少深入的研究,其中最經典的文章,Tetlock (2007)以Wall Street Journal專欄的文章內容作為樣本,發現高度悲觀的新聞情緒對於市場價格的下跌具有預測力,此外,特別高度的悲觀情緒也能預測市場的鉅量成交量。這位Columbia的教授還有後續相關的研究,有興趣的話可以看他的研究發表。實務上,其實也有很多公司提供相關的解決方案,只是我並不清楚他們對於市場調查的成效如何。 ***