前一天我們介紹 本益比、股價淨值比 與 殖利率 等評價股票的基本面指標,並示範如何從證交所與櫃買中心取得這些數據。不過需要注意的是,證交所與櫃買中心每日更新的本益比數據,是採用過去四季 EPS 算出來的「歷史本益比」而非「預估本益比」。這會有什麼問題呢?因為財務報表的數字是過去已經發生的事實,但是股價主要是反映對未來的預期。
我們在法人或券商提供的研究報告經常會看到所謂的「目標價」。如果是穩定獲利的公司,這些目標價往往都是經由 預估本益比 計算而來。我們在前一天已經介紹了本益比的算法是:
本益比 = 股價 / 每股盈餘
透過本益比的計算公式,我們可以得出股價的計算方式為:
股價 = 本益比 * 每股盈餘
當我們要評估公司未來的合理股價,就是給出一個 預期本益比 以及 預期每股盈餘:
合理股價 = 預期本益比 * 預期每股盈餘
如果是評價一個成長潛力或正在成長的公司,我們會關注與往年同期相比的季度 EPS 是否逐年成長。當公司最新的財報發布時,市場就會關心財報數字是否符合預期或發生意外。
獲得兩屆全美投資大賽冠軍的 馬克.米奈爾維尼(Mark Minervini)在暢銷書籍《超級績效:金融怪傑交易之道》中提到:「股價會因為兩種理由而發生變動:預期(anticipation)與意外(surprise)。每個價格走勢都是因為這兩種因素而變動:預期某新聞、某事件、某重要基本面發生變動,或是對於利多或利空意外事件產生反應。」
預期與意外會使股價發生變動,當公司發布財報時,市場的反應會有三種狀況:優於預期、符合預期 或 低於預期。如果財報數字出現顯著意外,負責追蹤的法人及分析師就會修正估值、更新目標價。當財報或月營收優於預期時,法人將上調獲利預估,EPS 被上修,目標價也跟著上調,此時容易吸引買盤進場,股價上漲機率高;當財報或月營收低於預期時,法人將下調獲利預估,EPS 下修使目標價也跟著下調,此時可能會有失望性賣壓,容易使股價下跌。
每季財報公佈的 EPS 是公司最重要的獲利數字之一,它會直接影響法人對公司的估值;而每月營收則是預估當季 EPS 重要的先行指標。對於自己實際持有的股票或考慮買進的標的,投資人應該關心公司公布財報與營收的時間,以及公司財報發布後市場的反應狀況。
要瞭解公司的營運狀況與獲利能力當然不僅每季 EPS 與每月營收數字,還需要針對公司的三大財務報告(綜合損益表、資產負債表、現金流量表)進行財務分析。不過這已經超出了本系列文的範圍,所以我們以投資人與市場最關心的公司每季 EPS、每月營收數字為例做說明。
會計是商業的語言,財務報告就是企業繳出的成績單。我國證交法規定,一般上市櫃公司應於每會計年度終了後 三個月內 公告 年度財務報告;於每會計年度第一季、第二季及第三季終了後 四十五日內 公告 季度財務報告;於 每月十日以前,公告並申報 上月份營運情形。一般上市櫃公司每月營收與財報公布日期如下:
財務報告的三大報表是「資產負債表」、「綜合損益表」以及「現金流量表」。其中 綜合損益表 表達企業某段期間經營損益之狀況,我們可以在公司發布每一季的綜合損益表中,找到公司的每股盈餘(EPS)。
過去企業編製財務報告主要是採用我國一般公認會計原則(ROC GAAP),為了提升國內企業及國際企業間財務報告之比較性,自 2013 年起,國內上市櫃、興櫃公司正式適用國際財務報導準則(IFRSs)。
在公開資訊觀測站彙總報表的 綜合損益表 頁面,可以按市場別、年度及季別,查詢上市、上櫃、興櫃或公開發行公司的綜合損益表。
公開資訊觀測站首頁 > 彙總報表 > 財務報表 > 採IFRSs後 > 綜合損益表
在「綜合損益表」頁面選擇「市場別」、「年度」以及「季別」,按下「查詢」後,就會列出符合條件查詢的資料。
假如我們要查詢上市公司在 2022 年(民國 111 年)第二季的 EPS,在「市場別」選「上市」、「年度」選「111」、「季別」選「2」,按下「查詢」後,就會列出所有上市公司在 2022 年(民國 111 年)第二季資料的綜合損益表彙整資訊,在彙整表格的最後一欄就可以找到「基本每股盈餘(元)」,即公司該季的 EPS。
我們可以打開終端機使用 curl
指令模擬表單請求:
$ curl --request POST \
--url https://mops.twse.com.tw/mops/web/ajax_t163sb04 \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'encodeURIComponent=1&step=1&firstin=1&off=1&isQuery=Y&TYPEK=sii&year=111&season=02'
curl
指令的 data-raw
選項代表 HTTP POST 的資料,其中最重要的是以下三個欄位:
TYPEK
:市場別,sii
表示上市公司;otc
表示上櫃公司,rotc
表示興櫃公司;pub
表示公開發行公司。year
:年度,以民國年表示。season
:季別,01
表示第一季;02
表示第二季;03
表示第三季;04
表示第四季。執行上述 curl
指令後會回應 HTML 格式是資料輸出綜合損益表彙整資訊。由於公開資訊觀測站目前並沒有以 API 的形式提供資料,因此我們必須實作爬蟲程式取得所需資料欄位。
首先我們新增一個 MopsScraperService
表示從公開資訊觀測站取得資料的服務。打開終端機,使用 Nest CLI 建立 MopsScraperService
:
$ nest g service scraper/mops-scraper --flat --no-spec
因為我們先前在專案建立了 common
lib,因此執行 Nest CLI 命令後可能會出現以下提示:
? Which project would you like to generate to?
選項會出現 src [ Default ]
和 common
,請選擇 src [ Default ]
即可。
然後 Nest CLI 會在 src/scraper
目錄下建立 mops-scraper.service.ts
檔案,並且將 MopsScraperService
加入至 ScraperModule
的 providers
設定。
開啟 src/scraper/mops-scraper.service.ts
檔案,在 MopsScraperService
實作 fetchQuarterlyEps()
方法,並指定 options
選項,可選擇市場別、年度與月份,以取得公司當季綜合損益表,擷取基本每股盈餘:
import * as _ from 'lodash';
import * as cheerio from 'cheerio';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
type Market = 'sii' | 'otc' | 'rotc' | 'pub';
@Injectable()
export class MopsScraperService {
constructor(private httpService: HttpService) { }
async fetchQuarterlyEps(options: { market: Market, year: number, quarter: number }) {
const { market, year, quarter } = options;
// 建立 FormData
const form = new URLSearchParams({
encodeURIComponent: '1',
step: '1',
firstin: '1',
off: '1',
isQuery: 'Y',
TYPEK: market,
year: numeral(year).subtract(1911).format(),
season: numeral(quarter).format('00'),
});
const url = 'https://mops.twse.com.tw/mops/web/t163sb04';
// 取得 HTML 頁面
const page = await firstValueFrom(this.httpService.post(url, form)).then(response => response.data);
// 使用 cheerio 載入 HTML 以取得表格的 table rows
const $ = cheerio.load(page);
// 遍歷每個 table row 並將其轉換成我們想要的資料格式
const data = $('.even,.odd').map((i, el) => {
const td = $(el).find('td');
const symbol = td.eq(0).text().trim(); // 公司代號
const name = td.eq(1).text().trim(); // 公司名稱
const eps = numeral(td.eq(td.length - 1).text().trim()).value(); // 基本每股盈餘(元)
return { symbol, name, eps, year, quarter };
}).toArray();
return _.orderBy(data, 'symbol', 'asc'); // 依股票代號排序
}
}
在 fetchQuarterlyEps()
方法中,參數需要指定 options
選項物件,包含以下內容:
market
:市場別,sii
表示上市;otc
表示上櫃;rotc
表示興櫃;pub
表示公開發行year
:年度quarter
:季別而 fetchQuarterlyEps()
方法以陣列形式回傳符合條件的公司 EPS 清單,每個陣列元素的物件包含以下內容:
symbol
:股票代號name
:股票名稱eps
:基本稅後盈餘year
:年度quarter
:季度完成後,我們只要呼叫 MopsScraperService
的 fetchQuarterlyEps()
方法,就可以按市場別、年度與季度取得公司當季 EPS。以下是上市公司在 2022 年第一季所公布之綜合損益表所取得的當季 EPS 為例:
[
{ symbol: '1101', name: '台泥', eps: 0.2, year: 2022, quarter: 1 },
{ symbol: '1102', name: '亞泥', eps: 0.76, year: 2022, quarter: 1 },
{ symbol: '1103', name: '嘉泥', eps: -0.12, year: 2022, quarter: 1 },
{ symbol: '1104', name: '環泥', eps: 0.82, year: 2022, quarter: 1 },
{ symbol: '1108', name: '幸福', eps: 0.14, year: 2022, quarter: 1 },
{ symbol: '1109', name: '信大', eps: 0.26, year: 2022, quarter: 1 },
{ symbol: '1110', name: '東泥', eps: 0.05, year: 2022, quarter: 1 },
{ symbol: '1201', name: '味全', eps: 0.19, year: 2022, quarter: 1 },
{ symbol: '1203', name: '味王', eps: 0.59, year: 2022, quarter: 1 },
{ symbol: '1210', name: '大成', eps: 0.69, year: 2022, quarter: 1 },
... 958 more items
]
在公開資訊觀測站的 採用IFRSs後每月營業收入彙總表 頁面,可以按市場別、年度及月份,查詢上市、上櫃、興櫃或公開發行公司的當月營收。
公開資訊觀測站首頁 > 彙總報表 > 營運概況 > 每月營收 > 採用IFRSs後每月營業收入彙總表
在「採用IFRSs後每月營業收入彙總表」頁面選擇「市場別」、「年度」以及「月份」,按下「查詢」後,查詢結果會以另跳視窗顯示。
假如我們要查詢國內上市公司在 2022 年(民國 111 年)8 月份的營收,在「市場別」選「上市」、「年度」選「111」、「月份」選「8」,按下「查詢」後,查詢結果以另跳視窗顯示,並進入以下頁面。
以國內上市公司在 2022 年(民國 111 年)8 月份的營收資料,URL 位址是:
https://mops.twse.com.tw/nas/t21/sii/t21sc03_111_6_0.html
解析網址,我們可以發現這個 URL 對應的查詢條件是:
https://mops.twse.com.tw/nas/t21/{市場別}/t21sc03_{年度}_{月份}_{公司類型}.html
以上 URL 對應查詢條件的值說明如下:
sii
表示上櫃;otc
表示上櫃;rotc
表示興櫃;pub
表示公開發行公司。0
表示本國公司;1
表示外國公司。由於公開資訊觀測站目前並沒有以 API 的形式提供當月營收資料,因此我們必須實作爬蟲程式取得所需資料欄位。
開啟 src/scraper/mops-scraper.service.ts
檔案,在 TwseScraperService
實作 fetchMonthlyRevenue()
方法,並指定 options
選項,可選擇市場別、年、月份與公司類型,以取得公司當月營收:
import * as _ from 'lodash';
import * as cheerio from 'cheerio';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
type Market = 'sii' | 'otc' | 'rotc' | 'pub';
@Injectable()
export class MopsScraperService {
constructor(private httpService: HttpService) { }
...
async fetchMonthlyRevenue(options: { market: Market, year: number, month: number, type: number }) {
const { market, year, month, type } = options;
const suffix = `${numeral(year).subtract(1911).value()}_${month}_${type}`;
const url = `https://mops.twse.com.tw/nas/t21/${market}/t21sc03_${suffix}.html`;
// 取得 HTML 並轉換為 Big-5 編碼
const page = await firstValueFrom(this.httpService.get(url, { responseType: 'arraybuffer' }))
.then(response => iconv.decode(response.data, 'big5'));
// 使用 cheerio 載入 HTML 以取得表格的 table rows
const $ = cheerio.load(page);
// 遍歷每個 table row 並轉換資料格式
const data = $('tr [align=right]')
.filter((i, el) => {
const th = $(el).find('th');
const td = $(el).find('td');
return (th.length === 0) && !!td.eq(0).text();
})
.map((i, el) => {
const td = $(el).find('td');
const symbol = td.eq(0).text().trim(); // 公司代號
const name = td.eq(1).text().trim(); // 公司名稱
const revenue = numeral(td.eq(2).text().trim()).value(); // 當月營收
return { symbol, name, revenue, year, month };
})
.toArray();
return _.sortBy(data, 'symbol'); // 依股票代號排序
}
}
在 fetchMonthlyRevenue()
方法中,需要指定 options
選項參數物件,包含以下內容:
market
:市場別。sii
表示上市;otc
表示上櫃;rotc
表示興櫃;pub
表示公開發行year
:年度month
:月份type
:公司類型。0
是本國公司;1
是外國公司而 fetchMonthlyRevenue()
方法以陣列形式回傳符合條件的公司當月營收清單,每個陣列元素的物件包含以下內容:
symbol
:股票代號name
:股票名稱revenue
:營業收入year
:年度month
:月份完成後,我們只要呼叫 MopsScraperService
的 fetchMonthlyRevenue()
方法,就可以按市場別、年份與月份取得公司當月營收。以下以國內上市公司在 2022 年 (民國 111 年) 8 月份的營收資料為例:
[
{ symbol: '1101', name: '台泥', revenue: 10689860, year: 2022, month: 8 },
{ symbol: '1102', name: '亞泥', revenue: 7069221, year: 2022, month: 8 },
{ symbol: '1103', name: '嘉泥', revenue: 182371, year: 2022, month: 8 },
{ symbol: '1104', name: '環泥', revenue: 605512, year: 2022, month: 8 },
{ symbol: '1108', name: '幸福', revenue: 390053, year: 2022, month: 8 },
{ symbol: '1109', name: '信大', revenue: 478276, year: 2022, month: 8 },
{ symbol: '1110', name: '東泥', revenue: 176935, year: 2022, month: 8 },
{ symbol: '1201', name: '味全', revenue: 2008672, year: 2022, month: 8 },
{ symbol: '1203', name: '味王', revenue: 546197, year: 2022, month: 8 },
{ symbol: '1210', name: '大成', revenue: 10578033, year: 2022, month: 8 }
... 882 more items
]
本系列文已正式出版為《Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰》。本書新增了全新內容和實用範例,為你提供更深入的學習體驗!歡迎參考選購,開始你的量化投資之旅!
天瓏網路書店連結:https://www.tenlong.com.tw/products/9786263336070